I am successfully using Azure AD and Office365 as a login provider in AspNet-Core Identity by using Microsoft.AspnetCore.Authentication.OpenIdConnect and calling
AddRemoteScheme<OpenIdConnectOptions, OpenIdConnectHandler>("AzureAD","Office 365",_=> { })
I then add a PostConfigureOptions handler for the OpenIdConnectOptions to set it up to work with Azure. This adds a Login with Office 365 button to the login page and is working, but there must be an easier way.
I was curious to see if Microsoft.Identity.Web could be used instead, but am unable to get it to work quite right in my test.
Using the Aspnet-Core templates for dotnet 6 in VS 2022 and selecting individual accounts for authentication you are scaffolded a project with AspNet-Core Identity configured to use an IdentityDbContext with local accounts.
When running the app and logging in, you see an empty list of external authentication providers and a link to Microsoft documentation on adding external authentication providers here:
To test the Microsoft.Identity.Web package, I am calling:
builder.Services.AddAuthentication().AddMicrosoftIdentityWebApp(config.GetSection("AzureAD"))
in Programs.cs
This works to add the authentication provider and I now get an "OpenIdConnect" button under "Use another service to log in". Clicking it results in a failure "Error loading external login information."
When trying to login by clicking the button, I receive "Error loading external login information.". Line 107 in ExternalLogin.cshtml.cs from Microsoft's Identity UI is always null:
var info = await _signInManager.GetExternalLoginInfoAsync();
Is it possible to provide the right arguments to .AddMicrosoftIdentityWebApp() such that it works with AspNet-Core Identity as an external authentication provider using minimal code and configuration?
After working through the source, the solution was simple. Here is a working extension method that will configure Microsoft.Identity.Web for use as an eternal authentication provider with AspNet-Core Identity:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddOffice365Identity<TContext,TUser>(
this IServiceCollection services,
IConfigurationSection config,
Action<IdentityOptions>? options = null)
where TContext : IdentityDbContext
where TUser : class
{
//Setup Default Identity
var identityBuilder = options == null ?
services.AddDefaultIdentity<TUser>()
: services.AddDefaultIdentity<TUser>(options);
identityBuilder.AddEntityFrameworkStores<TContext>();
//Add Microsoft.Identity.Web
services
.AddAuthentication()
.AddMicrosoftIdentityWebApp(config,displayName: "Office 365")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();
//This is the important part: Change the SignInScheme to External
//After ALL other configuration have run.
//Claim mapper maps all claims and adds the ClaimTypes.Email claim.
services
.AddOptions()
.PostConfigureAll<OpenIdConnectOptions>(o => {
o.SignInScheme = IdentityConstants.ExternalScheme;
o.ClaimActions.Add(new ClaimMapper());
});
return services;
}
}
Running this you will end up with a working "Office 365" configuration assuming your appconfig has the usual:
"AzureAd": {
"CallbackPath": "/signin-oidc",
"ClientSecret": "[client_secret]",
"ClientId": "[client_id]",
"Domain": "[domain]",
"Instance": "https://login.microsoftonline.com/",
"SignedOutCallbackPath": "/signout-callback-oidc",
"TenantId": "[Tenant Id]"
}