Multiple Authentication Schemas (AAD/AAD B2C) in Asp.Net Core
In this article, we will see how to use both Azure AD and Azure AD B2C authentication schemas in an Asp.Net Core Web App or Web API.
Let’s assume a scenario, A web API is hosted on Azure App Service enabled with managed identity. API needs to be authenticated and authorized with either AAD or AAD B2C.
Prerequisites:
- Register an application in both Azure AD and Azure AD B2C While Azure AD B2C app requires client secret, Azure AD app doesn't as application gets its token using managed identity.
- Enable managed identity for Azure App Service.
- Assign a managed identity access to Azure AD application. Access to managed identity can be assigned via PowerShell or Microsoft Graph. This option is not available in Azure Portal.
PowerShell Script to assign a managed identity access to app role
# Install-Module AzureAD
# Import-Module AzureAD
$tenantID = '<tenant-id>'
$webAppName = '<web-app-name>'
$resourceGroupName = '<resource-group-name-containing-web-app>'
$serverApplicationName = '<server-application-name>'
$appRoleName = '<app-role-name>'
$managedIdentityObjectId = (Get-AzWebApp -ResourceGroupName $resourceGroupName -Name $webAppName).identity.principalid
Connect-AzureAD -TenantId $tenantID
$serverServicePrincipal = (Get-AzureADServicePrincipal -Filter "DisplayName eq '$serverApplicationName'")
$serverServicePrincipalObjectId = $serverServicePrincipal.Id
$appRoleId = ($serverServicePrincipal.AppRoles | Where-Object {$_.Value -eq $appRoleName }).Id
New-AzureADServiceAppRoleAssignment `
-ObjectId $managedIdentityObjectId `
-Id $appRoleId `
-PrincipalId $managedIdentityObjectId `
-ResourceId $serverServicePrincipalObjectId
Asp.Net Code to authenticate an application with AAD and AAD B2C auth schemas.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com",
"TenantId": "00000000-0000-0000-0000-000000000aad",
"ClientId": "11111111-1111-1111-1111-111111111aad"
},
"AzureAdB2C": {
"Instance": "https://login.microsoftonline.com",
"TenantId": "00000000-0000-0000-0000-000000000b2c",
"ClientId": "11111111-1111-1111-1111-111111111b2c",
"ClientSecret": ""
}
}
Create a model for AzureAdB2COptions as below:
public class AzureAdb2COptions {
public string Instance {get; set;}
public string TenantId {get; set;}
public string ClientId {get; set;}
public string ClientSecret {get; set;}
}
Add the authentication schemas to configuration services of startup.cs or program.cs file (depends on the .net core version) as below. ‘AzureAd’ is the authentication schema name for AzureAd, ‘Bearer’ is the auth schema name for AzureAdB2C. AddMicrosoftIdentityWebApi will take only one option and by default, it names the authentication schema as Bearer.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("AzureAd", options => {
options.Audience = Configuration["AzureAd:ClientId"];
options.Authority = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}";
})
.AddMicrosoftIdentityWebApi(options => {
Configuration.Bind("AzureAdB2C", options);
options.TokenValidationParameters.NameClaimType = "name"; },
options => { Configuration.Bind("AzureAdB2C", options); });
services.Configure<AzureAdB2COptions>(Configuration.GetSection("AzureAdB2C"));
Add the authorization policy to configuration services as below.
services.AddAuthorization(options => {
//Applies to [Authorize] header in controller - ie both AAD/B2C token accepted.
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AzureAd", "Bearer")
.Build();
//[Authorize(Policy = "AADAuth")] - When this header used in controller, only AAD token accepted.
options.AddPolicy("AADAuth", new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AzureAd")
//.RequireClaim("role", "admin") //could be role/scope
.Build());
});
Add authentication and authorization in configure class.
app.UseAuthentication();
app.UseAuthorization();
Finally add authorization to the controller.
//Only AAD token can access this endpoint.
[Authorize(Policy = "AADAuth")]
[Route("api/[controller]")]
[ApiController]
//Both AAD and B2C token can access this endpoint.
[Authorize]
[Route("api/[controller]")]
[ApiController]
Refer to the below GitHub repo for detailed code.