How to Use the Azure Active Directory Authentication for MVC / Razor Page Applications
This guide 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.
Two different alternative approaches for AzureAD integration will be demonstrated for better coverage.
- AddOpenIdConnect: This approach uses default OpenIdConnect which can be used for not only AzureAD but for all OpenId connections.
- AddMicrosoftIdentityWebAppAuthentication: This approach uses newly introduced Microsoft.Identity.Web nuget package to replace AddAzureAD.
AddAzureAD: This approach uses Microsoft AzureAD UI nuget package which is very popular when users search the web about how to integrate AzureAD to their web application.Now marked Obsolete (see https://github.com/aspnet/Announcements/issues/439).
There is no difference in functionality between these approaches. AddAzureAD is an abstracted way of OpenIdConnection (source) with predefined cookie settings.
However there are key differences in integration to ABP applications because of default configured signin schemes which will be explained below.
1. AddOpenIdConnect
If you don't want to use an extra nuget package in your application, you can use the straight default OpenIdConnect which can be used for all OpenId connections including AzureAD external authentication.
You don't have to use appsettings.json
configuration but it is a good practice to set AzureAD information in the appsettings.json
.
To get the AzureAD information from appsettings.json
, which will be used in OpenIdConnectOptions
configuration, simply add a new section to appsettings.json
located in your .Web project:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-client-id>",
"Domain": "domain.onmicrosoft.com",
"CallbackPath": "/signin-azuread-oidc"
}
Then, In your .Web project; you can modify the ConfigureAuthentication
method located in your ApplicationWebModule with the following:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
... //Omitted other third party configurations
.AddOpenIdConnect("AzureOpenId", "Azure Active Directory OpenId", options =>
{
options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
options.ClientId = configuration["AzureAd:ClientId"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.ClientSecret = configuration["AzureAd:ClientSecret"];
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("email");
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
});
}
Don't forget to:
- Add
options.Scope.Add("email");
since default signin scheme isAzureADOpenID
.- Add
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
. Mapping this to ClaimTypes.NameIdentifier is important since default SignIn Manager behavior uses this claim type for external login information.
And that's it, integration is completed. Keep on mind that you can connect any other external authentication providers.
2. Alternative Approach: AddMicrosoftIdentityWebApp
With .Net 5.0, AzureAd is marked obsolete and will not be supported in the near future. However its expanded functionality is available in microsoft-identity-web packages.
Add (or replace with) the new nuget package Microsoft.Identity.Web nuget package](https://www.nuget.org/packages/Microsoft.Identity.Web/).
In your .Web project; you update the ConfigureAuthentication
method located in your ApplicationWebModule with the following while having the AzureAd appsettings section as defined before:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
... //Omitted other third party configurations
.AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"));
context.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
options.ClientId = configuration["AzureAd:ClientId"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.ClientSecret = configuration["AzureAd:ClientSecret"];
options.RequireHttpsMetadata = false;
options.SaveTokens = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SignInScheme = IdentityConstants.ExternalScheme;
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
});
}
And that's all to add new Microsoft-Identity-Web.
Don't forget to:
- Add
options.SignInScheme = IdentityConstants.ExternalScheme
since default signin scheme isAzureADOpenID
.- Add
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
. Mapping this to ClaimTypes.NameIdentifier is important since default SignIn Manager behavior uses this claim type for external login information.
Keep in mind that Microsoft-Identity-Web is relatively new and keeps getting new enhancements, features and documentation.
3. Obsolete Alternative Approach: AddAzureAD
This approach uses the most common way to integrate AzureAD by using the Microsoft AzureAD UI nuget package.
If you choose this approach, you will need to install Microsoft.AspNetCore.Authentication.AzureAD.UI
package to your .Web project. Also, since AddAzureAD extension uses configuration binding, you need to update your appsettings.json file located in your .Web project.
Updating appsettings.json
You need to add a new section to your appsettings.json
which will be binded to configuration when configuring the OpenIdConnectOptions
:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-client-id>",
"Domain": "domain.onmicrosoft.com",
"CallbackPath": "/signin-azuread-oidc"
}
Important configuration here is the CallbackPath. This value must be the same with one of your Azure AD-> app registrations-> Authentication -> RedirectUri.
Then, you need to configure the OpenIdConnectOptions
to complete the integration.
Configuring OpenIdConnectOptions
In your .Web project, locate your ApplicationWebModule and modify ConfigureAuthentication
method with the following:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
... //Omitted other third party configurations
.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.CodeIdToken;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters.ValidateIssuer = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.SignInScheme = IdentityConstants.ExternalScheme;
options.Scope.Add("email");
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
});
}
Don't forget to:
- Add
.AddAzureAD(options => configuration.Bind("AzureAd", options))
after.AddAuthentication()
. This binds your AzureAD appsettings and easy to miss out.- Add
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
. Mapping this to ClaimTypes.NameIdentifier is important since default SignIn Manager behavior uses this claim type for external login information.- Add
options.SignInScheme = IdentityConstants.ExternalScheme
since default signin scheme isAzureADOpenID
.- Add
options.Scope.Add("email")
if you are using v2.0 endpoint of AzureAD since v2.0 endpoint doesn't return the
You are done and integration is completed.
The Source Code
You can find the source code of the completed example here.
FAQ
Help!
GetExternalLoginInfoAsync
returnsnull
! (Using obsolute AddAzureAD)There can be 2 reasons for this;
You are trying to authenticate against wrong scheme. Check if you set SignInScheme to
IdentityConstants.ExternalScheme
:options.SignInScheme = IdentityConstants.ExternalScheme;
Your
ClaimTypes.NameIdentifier
isnull
. Check if you added claim mapping:JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
Help!
GetExternalLoginInfoAsync
returnsnull
! (Using AddMicrosoftIdentityWebAppAuthentication)- Pass cookieScheme parameter as null. (See this issue).
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 addoptions.Scope.Add("email");
to your openid configuration.
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 keep getting AADSTS700051: The response_type 'token' is not enabled for the application. error!
- This error occurs when you request token (access token) along with id_token without enabling Access tokens on Azure portal app registrations. Simply tick Access tokens checkbox located on top of ID tokens to be able to request token aswell.
Help! I keep getting AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret error!
This error occurs when you request code along with id_token. You need to add client secret on azure portal app registrations, under Certificates & secrets menu. Afterwards, you need to add openid configuration option like:
options.ClientSecret = "Value of your secret on azure portal";
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 get page not found error on redirection to https://login.live.com/oauth20_authorize.srf?!
- Probably you are trying to login with Microsoft account to Azure portal instead of the Azure AD account. Try azure AD user account for login instead of microsoft account. You can also check the answers for this question and this question for more details.
May 2021 Update
- AddOpenIdConnect: Removed
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
and addedsub
claim mapping in ClaimActions rather than global mapping. - Updated
AddMicrosoftIdentityWebAppAuthentication
toAddMicrosoftIdentityWebApp
. - Updated OpenIdConnect and AddMicrosoftIdentityWebApp configurations.
- Obsolete approach moved to third place in the list.
Comments
EL HADI Abdelhalim 216 weeks ago
Hi,
I managed to set up your code on a third party architecture (separate IdentityServer).
When I log in from IdentityServer it works fine. but when I log in from Blazor I have the following error: Page not found .... https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=51483342-085c-4d86-bf88-cf50c7252078&scope=openid+profile+email+offline_access&response_mode=form_post&
Any Idea ?
Jose M Gonzalez 213 weeks ago
AADSTS700054: response_type 'id_token' is not enabled for the application. Issue using 2. Alternative Approach: AddOpenIdConnect. Go to Azure AD Portal. --> application registration and select your app --> authentication --> Check Implicit grant 'ID tokens'
cresnikv 208 weeks ago
Is this also possible with normal Active Directory? Azure AD is not on option for us.
Galip Tolga ERDEM 208 weeks ago
Hello cresnikv, No it is totally different flows and different directory services. You can use LDAP for Active Directory. Did you check https://github.com/abpframework/abp/issues/3638 and https://github.com/abpframework/abp/issues/4165?
Jake Chu 162 weeks ago
Hi, I got the error. UserFriendlyException: Self-registration is disabled for this application. Please contact the application administrator to register a new user.
sharonniaz93@gmail.com 100 weeks ago
Is there any who have solved this issue I am getting the same error
Aluminum2work@outlook.com 131 weeks ago
Hi! We followed the second approach, using AddMicrosoftIdentityWebApp, but it seems like the token retrieved through this method will replace the bear token. Is there any other method to get bear on behalf of user instead of https://support.abp.io/QA/Questions/1672/Access-Token-Conflict-while-Integrating-with-Microsoft-Graph We posted our situation here https://github.com/abpframework/abp/issues/12911 Thanks!
Galip Tolga ERDEM 131 weeks ago
Hello. I have no experience on Microsoft Graph and I don't think it is related to Azure AD integration. This post explains 3rd party login with Azure AD and how ABP Identity module interacts with 3rd party login using Microsoft Identity library.
ian.bates@Aecom.com 124 weeks ago
Why is
RequireHttpsMetadata
set to false? It wouldn't be advised for a production system or would it?sharonniaz93@gmail.com 100 weeks ago
Hi, I got the error. UserFriendlyException: Self-registration is disabled for this application. Please contact the application administrator to register a new user.
ian.bates@Aecom.com 99 weeks ago
How is this going to work with ABP 6+? I no longer see the same code while using OpenIddict. ConfigureAuthentication now looks like this:
Galip Tolga ERDEM 99 weeks ago
You can still use
AddOpenIdConnect
extension method for authorization.ian.bates@Aecom.com 99 weeks ago
Thanks - we found that this works:
Lee Richardson 94 weeks ago
This worked for me Ian, thank you thank you thank you!
Dicky.tech@gmail.com 78 weeks ago
What is the best way to integrate a desktop application that generates tokens via azure ad ?
mjnamjkaze@gmail.com 72 weeks ago
thanks man, i can success setup
Anthony Albutt 21 weeks ago
Thanks, this has ben working well for one tenant. How would I change this to use WithDynamicOptions, as in social media authentication
WithDynamicOptions options.WithProperty(x => x.AzureAd.TenantId); options.WithProperty(x => x.AzureAd.ClientId); options.WithProperty(x => x.AzureAd.ClientSecret, isSecret: true);
Thanks again
Nacho González 13 weeks ago
Hi, I tried the authentication with a new Blazor Server application and Azure Entra ID (Open ID) but I am receiving this error:
OpenIdConnectProtocolException: IDX21314: OpenIdConnectProtocol requires the jwt token to have an 'iss' claim. The jwt did not contain an 'iss' claim, jwt: '[Security Artifact of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden..
I am using ABP 8.2.2 and the following configuration: