I am struggling to understand how role based authorization is working in Azure app services. It works for the user, but not service principals (app registrations). My setp:
- I create app roles for app registration:
- I assign users and principals to some roles:
- I assign this app registration to some app service with Microsoft as provider
In my code I have the following setup:
- Install package
Microsoft.Indeitity.Web
(2.17 version, currently latest) - Program.cs:
// Makes the roles come in 'role' claim
System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddAuthorization();
builder.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// Sets up the claim name which will be used by Authorize attribute
options.TokenValidationParameters.RoleClaimType = "roles";
});
- My controller:
[ApiController]
[Route("[controller]")]
public class Test : ControllerBase
{
[HttpGet("Reader")]
[Authorize(Roles = "Reader")]
public IActionResult Reader() =>
Ok("Success")
}
- My appSettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "{mytenant}.onmicrosoft.com",
"TenantId": "{tenantid}",
"ClientId": "{AR client id}",
"ClientSecret": "{AR client secret}",
"Scopes": "access_as_user",
"CallbackPath": "/signin-oidc"
},
Now when I deploy this app to app service, for the user Accessing Reader controller is working as expected.
I also created claims controller:
[HttpGet("GetClaims")]
public IActionResult GetClaims()
{
var sb = new System.Text.StringBuilder("Claims:\r\n\r\n");
User.Claims.ToList().ForEach(_ => sb.AppendLine($"Key: '{_.Type}'\tValue: '{_.Value}'"));
sb.AppendLine();
sb.AppendLine("\r\n\r\nHeaders:\r\n\r\n");
ClaimsPrincipal.Current?.Claims.ToList().ForEach(_ => sb.AppendLine($"Type: '{_.Type}'\tSubject: '{_.Subject}'\tIssuer: '{_.Issuer}'\tValueType: '{_.ValueType}'\tValue: '{_.Value}'"));
return Ok(sb.ToString());
}
And for the user I can see roles coming up from the claim:
But when I try to access it with service principal (for example via logic app), it gives me empty claims, what means that authorization is also not working, while accessing Reader
controller:
I believe I am missing something small in here and it would be hard to believe MS did not implemented service principal authorization based on roles.
As other workaround I believe I would create a middleware to map claims from the token for service principals, but I would really like to avoid this if possible.