You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/en/How-To/Azure-Active-Directory-Auth...

6.8 KiB

How to Use the Azure Active Directory Authentication for MVC / Razor Page Applications

Introduction

This post demonstrates how to integrate AzureAD to an ABP application that enables users to sign in using OAuth 2.0 with credentials from Azure Active Directory.

Adding Azure Active Directory is pretty straightforward in ABP framework. Couple of configurations needs to be done correctly.

There will be two samples of connections for better coverage;

  • AddAzureAD (Microsoft.AspNetCore.Authentication.AzureAD.UI package)
  • AddOpenIdConnect (Default Microsoft.AspNetCore.Authentication.OpenIdConnect package)

Sample Code

https://github.com/abpframework/abp-samples/tree/master/aspnet-core/BookStore-AzureAD

Setup

Update your appsettings.json in your .Web application and add the following section filled with your AzureAD application settings.

  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<your-tenant-id",
    "ClientId": "<your-client-id>",
    "Domain": "domain.onmicrosoft.com",
    "CallbackPath": "/signin-azuread-oidc"	
  }

AddAzureAD

Update your appsettings.json

Install Microsoft.AspNetCore.Authentication.AzureAD.UI package to your .Web application.

In your .Web application, add the following section filled with your AzureAD application settings. Modify ConfigureAuthentication method of your BookStoreWebModule with the following:

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
            context.Services.AddAuthentication()
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = false;
                    options.ApiName = "Acme.BookStore";
                })
            .AddAzureAD(options => configuration.Bind("AzureAd", options));

            context.Services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
            {
                options.Authority = options.Authority + "/v2.0/";         
                options.ClientId = configuration["AzureAd:ClientId"];
                options.CallbackPath = configuration["AzureAd:CallbackPath"];
                options.ResponseType = OpenIdConnectResponseType.IdToken;
                options.RequireHttpsMetadata = false;

                options.TokenValidationParameters.ValidateIssuer = false; 
                options.GetClaimsFromUserInfoEndpoint = true;
                options.SaveTokens = true;
                options.SignInScheme = IdentityConstants.ExternalScheme;

                options.Scope.Add("email");
            });
		}

AddOpenIdConnect

Modify ConfigureAuthentication method of your BookStoreWebModule with the following:

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);

            context.Services.AddAuthentication()
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = false;
                    options.ApiName = "BookStore";
                })
                .AddOpenIdConnect("AzureOpenId", "AzureAD", options =>
                 {
                     options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"];
                     options.ClientId = configuration["AzureAd:ClientId"];
                     options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                     options.CallbackPath = configuration["AzureAd:CallbackPath"];
                     options.RequireHttpsMetadata = false;
                     options.SaveTokens = true;
                     options.GetClaimsFromUserInfoEndpoint = true;
                 });
        }

FAQ

  • Help! GetExternalLoginInfoAsync returns null!

    • There can be 2 reasons for this;

      1. You are trying to authenticate against wrong scheme. Check if you set SignInScheme to IdentityConstants.ExternalScheme:

        options.SignInScheme = IdentityConstants.ExternalScheme;
        
      2. Your ClaimTypes.NameIdentifier is null. Check if you added claim mapping:

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
        
  • Help! I keep getting AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application error!

    • If you set your CallbackPath in appsettings as:

        "AzureAd": {
          ...
          "CallbackPath": "/signin-azuread-oidc"	
        }
      

      your Redirect URI of your application in azure portal must be with domain like https://localhost:44320/signin-azuread-oidc, not only /signin-azuread-oidc.

  • Help! I am getting System.ArgumentNullException: Value cannot be null. (Parameter 'userName') error!

    • This occurs when you use Azure Authority v2.0 endpoint without requesting email scope. Abp checks unique email to create user. Simply add

      options.Scope.Add("email");
      

      to your openid configuration.

  • How can I debug/watch which claims I get before they get mapped?

    • You can add a simple event under openid configuration to debug before mapping like:

      options.Events.OnTokenValidated = (async context =>
      {
      	var claimsFromOidcProvider = context.Principal.Claims.ToList();
      	await Task.CompletedTask;
      });
      
  • I want to debug further, how can I implement my custom SignInManager?

    • You can check [Customizing SignInManager in Abp Framework](link here) post.
  • I want to add extra properties to user while they are being created. How can I do that?

    • You can check Customizing Login Page in Abp Framework post.
  • Why can't I see External Register Page after I sign in from external provider for the first time?

    • ABP framework automatically registers your user with supported email claim from your external authentication provider. You can change this behavior by [Customizing Login Page in Abp Framework](will be link here).