fbpx

06. IdentityServer4 External Providers .NET Core 3.1

You can find the project here.

Standard Protocols

All Identity Providers are supported using standard protocols like OpenID Connect, OAuth2, SAML2 and WS-Federation. This could be Okta, it could be Auth0, could be proprietary IdP of a client, could be another IdentityServer4. Take a look at the list of out-of-the-box extensions for “AuthenticationBuilder” for big providers like Azure AD, Microsoft Account, Google, Facebook, Twitter, etc here https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationbuilder?view=aspnetcore-2.2

Setting up the usual OpenID Connect OIDC middleware is enough for most of the providers to get you going. Almost all providers nowadays provide OIDC, some as a second option alongside SAML2 and/or WS-Fed.

As IdentityServer4 is OIDC Identity Provider you can actually set up one IdentityServer4 instance to be an external provider for another IdentityServer4 instance using OIDC middleware. As long as there is a single root node, all Identity Servers connected this way can achieve SSO.

 

Azure AD Example

I will continue from my last tutorial. Open the “Quickstart” solution in Visual Studio.

Open the “Startup.cs” in project root and navigate right above the “AddIdentityServer” service registration. Add the authentication middleware for AzureAD like so:

services.AddAuthentication()
	.AddOpenIdConnect("azuread", "Azure AD", options => Configuration.Bind("AzureAd", options));

services.Configure<OpenIdConnectOptions>("azuread", options =>
            {
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProviderForSignOut = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Account/Logout");
                        return Task.FromResult(0);
                    }
                };
            });

Now open the “appsettings.json” in project root and modify it to add the Azure AD configuration we are using and binding in “Startup” like so:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=IdentityServerQuickstart.NetCore3.1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "AzureAd": {
    // Authority/MetadataAddress format (https://{instance}/{tenantId}/...
    "Authority": "https://login.microsoftonline.com/0366c849-xxxx-xxxx-xxxx-adcc0ccf2170/oauth2/v2.0/",
    "MetadataAddress": "https://login.microsoftonline.com/0366c849-xxxx-xxxx-xxxx-adcc0ccf2170/.well-known/openid-configuration",
    "ClientId": "7adeb3b0-xxxx-xxxx-xxxx-a6bc5aa756da",
    "CallbackPath": "/signin-oidc"
  }
}

Note: You must get your “TenantId” and “ClientId” (aka “ApplicationId”) from the Azure portal. Here are the official docs on how to create an app in Azure AD https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app

Tip: You will need the ReturnUrl for app registration. For this demo, the return URL is http://localhost:5000/signin-oidc

 

Okta Example

Open the “Startup.cs” in project root and navigate right below the “AddAzureAD” and add:

.AddOpenIdConnect("okta", "Okta", options => Configuration.Bind("Okta", options));

Also add the OpenIdConnectOptions service configuration like so:

services.Configure<OpenIdConnectOptions>("okta", options =>
            {
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProviderForSignOut = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Account/Logout");
                        return Task.FromResult(0);
                    }
                };
            });

So full code including Azure Ad and Okta looks like so:

services.AddAuthentication()
                .AddOpenIdConnect("azuread", "Azure AD", options => Configuration.Bind("AzureAd", options))
                .AddOpenIdConnect("okta", "Okta", options => Configuration.Bind("Okta", options));
            services.Configure<OpenIdConnectOptions>("azuread", options =>
            {
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProviderForSignOut = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Account/Logout");
                        return Task.FromResult(0);
                    }
                };
            });
            services.Configure<OpenIdConnectOptions>("okta", options =>
            {
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProviderForSignOut = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Account/Logout");
                        return Task.FromResult(0);
                    }
                };
            });

Now open the “appsettings.json” in project root and modify it to add the Okta configuration we are using and binding in “Startup” like so:

"Okta": {
	"Authority": "https://dev-xxxxxx-admin.oktapreview.com",
	"ClientId": "0oakhxxxxxxxxxxaX0h7",
	"CallbackPath": "/signin-oidc-okta"
}

Note: You must get your “Authority” and “ClientId” from Okta. Here are the official docs how to create an Okta app https://developer.okta.com/docs/guides/add-an-external-idp/microsoft/register-app-in-okta/

Tip: You will need the ReturnUrl for app registration. For this demo, the return URL is http://localhost:5000/signin-oidc-okta

 

Modify the user auto-provisioning process

Because we added the “IsEnable” custom property in the previous tutorial the auto-provisioned user will by default have value “false” (disabled user) and the external provider login will fail. We need to slightly modify the automatic user creation process for external providers to set the “IsEnabled” flag to “true”. Navigate to “Quickstart/Account/ExternalController.cs” and open it.

Find the “AutoProvisionUserAsync” method and modify the line that instantiates new user. We need to modify it to set the “IsEnabled” user property to “true” like so:

var user = new ApplicationUser
{
	UserName = Guid.NewGuid().ToString(),
	Email = email,
	IsEnabled = true
};

Now run the IdentityServer4 and try to sign in with Azure AD or Okta. If the local user exists with the same username or email as the external user (from Azure AD or Okta in our example) the matching process will link the external user with local user and the new local user will not be created. For other scenarios (no match) the auto-provisioning process will create a new local user and link it with the external user. I logged in using Okta and the new local user was auto-provisioned. Notice that my name was automatically populated from the claims provided by Okta. These are the claims of the external user now set to the local user.

Too easy

Now that was super easy, wasn’t it? Adding any standard Identity Provider shouldn’t pose any challenge as the method is pretty much the same. In my next tutorial I will start tackling one of the important features which are Multi-Factor Authentication MFA aka 2FA if there are two factors. Stay fresh!

You can find the project here.

Support

For direct assistance schedule a technical meeting with Ivan to talk about your requirements. For a general overview of our services and a live demo schedule a meeting with Maja.

Comments
  • Dung Nguyen says:

    Great turtorials,
    When I login to Azure sucess, redirect to my web.
    Everything is fine, but I can not see ‘User name’ (like alice) in login panel
    Please help me!
    Thanks in advance.

    • deblokt says:

      Hi. It seems that you need to set Name claim property on the ClaimsIdentity to match the Azure name claim. You can do that in Startup, just add an event to intercept the auth process on the external provider middleware registration for Azure.

  • Hari says:

    Hi,
    How can we implement if we have multiple Okta urls(ex: If we want to implement one IdentityServer app which can interact with multiple Okta client urls)?

    Thanks,

    • deblokt says:

      Hi Hari,

      In that scenario you need to register multiple OpenID Connect middlewares configured to target different Okta configurations.

      • Hari says:

        Hi Deblokt,

        Many thanks for the update. Can you please share if you have any sample code on this?
        Thanks,
        Hari.

        • deblokt says:

          Hi,

          In the example above we have two OpenID Connect middlewares registered. You just need to add Okta configs in your appsettings.json file and change the middleware using Azure AD in our example to use the second Okta configuration instead. This will give you two OpenID Connect middlewares both using Okta.

  • Jan says:

    First, thank you very much for the excellent tutorial.

    When using Azure AD, you get an error “AADSTS700054: response_type ‘id_token’ is not enabled for the application”.
    To fix this, you will need to go to the Azure portal and navigate to your App Registration and go to Authentication/Advanced Settings/Implicit grant and enable the “ID Tokens” check box.

Comments are closed.