Home > Enterprise >  ASP.NET Core MVC, Azure Role based Authentication - Authorize Attribute failure - no Role claim retu
ASP.NET Core MVC, Azure Role based Authentication - Authorize Attribute failure - no Role claim retu

Time:11-22

I am to build an ASP.NET Core webapp with .NET6. I have followed the Azure-Sample repository on GitHub

I have adjusted the appsettings.json with AzureAd section:

 "AzureAd": {
        "ClientId": "my-client-guid",
        "TenantId": "my-tenant-guid",
        "Domain": "my-tenant-name",
        "ClientSecret": "my-client-secret-guid",
        "Instance": "https://login.microsoftonline.com/",
        "Scopes": "user.read,profile,openid",
        "ClientCapabilities": [ "cp1" ],
        "CallbackPath": "/signin-oidc"
    }
  • ClientId and TenantId fields are fed from Azure Portal AD Application Blade , where I have created the App Registration. On the detail page of this application is the Overview blade - which gives reference to ClientId, TenantId.
  • ClientSecret has been created at the same page, at Certificates & secrets blade.
  • Domain is provided from Azure Domain list blade

User roles (Reader/Admin) have been created at the above (Azure AD Application) page.

Still on the same page:

  • Authentication Tab a new Web platform was added with Redirect URI https://localhost:7293/signin-oidc. As for Front-Channel logout https://localhost:44321/signout-oidc was provided.
  • ID Tokens is ticked under Implicit grant and hybrid flows section
  • Single Tenant option (only allow organization accounts)
  • Public flows disabled

In Enterprise Applications my application is listed. In the details view of the application:

  • Properties tab: Assignment required is set to false. I do not want to preassign users to the app
  • Visible to users: set to false
  • Users and groups tab: the Security groups have been created for which the Roles (Reader/Admin) are assigned to. Members are assigned to Security groups. For testing purposes I have added my Azure AD User as well with Reader role.
  • Single Sign-On: Here I see the following message: The single sign-on configuration is not available for this application in the Enterprise applications experience. {{my-application-name}} was created using the App registrations experience
  • Sign-in logs: oddly enough I see here my logins, so I assume that the role claims are not transferred to my WebApplication, since I get Unauthorized error upon opening the view at the controller.

The WebApp references

Config sections I have mapped to a model so I can inject if needed as IOptions MicrosoftGraphConfiguration to map GraphApiUrl:

public class MicrosoftGraphConfiguration
{
    public const string ConfigurationName = "MicrosoftGraph";
    public string? GraphApiUrl { get; set; }
}

AzureAdConfiguration to map AzureAd configurations

public class AzureAdConfiguration
{
    public const string ConfigurationName = "AzureAd";
    public string? ClientId { get; set; }
    public string? TenantId { get; set; }
    public string? ClientSecret { get; set; }
    public string? Domain { get; set; }
    public string? Instance { get; set; }
    public string? BaseUrl { get; set; }
    public string[]? ClientCapabilities { get; set; }
    public string[]? Scopes { get; set; }
    public string? CallbackPath { get; set; }
}

Program.cs to add configure and add services to the DI container, etc.

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<MicrosoftGraphConfiguration>(builder.Configuration.GetSection(MicrosoftGraphConfiguration.ConfigurationName));
builder.Services.Configure<AzureAdConfiguration>(builder.Configuration.GetSection(AzureAdConfiguration.ConfigurationName));

JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, subscribeToOpenIdConnectMiddlewareDiagnosticsEvents: true)
    .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "User.Read" })
    .AddInMemoryTokenCaches();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AssignmentToReaderRoleRequired", policy => policy.RequireRole("Reader"));
    options.AddPolicy("AssignmentToAdminRoleRequired", policy => policy.RequireRole("Admin"));
});

builder.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters.RoleClaimType = "roles";
});

// Add services to the container.
builder.Services.AddControllersWithViews(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();

    options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Controller actions for testing purposes:

[HttpGet]
public async Task<IActionResult> IndexAsync()
{
    GetTenantDetailsResult tenantDetails = new();
    
    if (this.tenantService != null)
    {
        tenantDetails = await this.tenantService.GetTenantDetailsAsync();
    }

    return View(new TenantViewModel(tenantDetails));
}

[HttpGet]
[Route("/Reader")]
[Authorize(Policy = AuthorizationPolicies.AssignmentToReaderRoleRequired)]
public IActionResult Reader()
{
    var asd = User;
    return Ok("He is a reader");
}

[HttpGet]
[Route("/Admin")]
[Authorize(Policy = AuthorizationPolicies.AssignmentToAdminRoleRequired)]
public IActionResult Admin()
{
    return Ok("He is an admin");
}

Launching the app (without cookies - incognito) redirects me to https://login.microsoft.com/{my-tenantId}/oauth2/authorize?.... Once logged in, endpoints without the [Authorize(...)] attribute works well.

Opening any of the above endpoints (where [Authorize] attribute is in place) gives me HTTP 404 and redirects to: https://localhost:7293/MicrosoftIdentity/Account/AccessDenied?ReturnUrl=/reader

According to the docs, the Roles Claim should be sent together with any ID Tokens. "These assigned app roles are included with any token that's issued for your application, either ... or ID tokens when your app is signing in a user"

I am unsure -thus my questions are- if:

  • The roles claim are not sent?
  • The roles claim are sent but cannot be parsed
  • How to proceed with the debugging to find out the issue

As a weird behavior.. if I open the application/Reader endpoint in Incognito/InPrivate browser window, I get a HTTP 500 response code after the login with the following error details: MsalServiceException: A configuration issue is preventing authentication - check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details. Original exception: AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app

Even though the ClientSecret is as above mentioned, I believe it is correct. A reason for the Http 500 might be that currently I do not maintain an endpoint for https://localhost:7293/signin-oidc

I appreciate helps, as I am a bit lost here for 3rd day.

Thx

CodePudding user response:

I tried to reproduce the same in my environment and got below results:

I registered one Azure AD application named WebApp-RolesClaims and followed the same steps mentioned on GitHub

I have adjusted the appsettings.json file with same values as you like below:

 "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "Domain": "my-tenant-name",
        "TenantId": "my-tenant-guid",
        "ClientId": "my-client-guid",
        "CallbackPath": "/signin-oidc"
        "SignedOutCallbackPath ": "/signout-callback-oidc",
        "Scopes": "user.read,profile,openid",
         // To call an API
        "ClientSecret": "my-client-secret-guid"        
    }

enter image description here

Now I ran the sample, and it took me to Microsoft login page.

When I signed in with Azure AD user credentials, I got consent screen like below:

enter image description here

After accepting the above consent, it gave me same exception as you like below:

enter image description here

As the error message says, it usually occurs if you include Client secret ID instead of Client secret value.

To resolve the error, you need replace "ClientSecret" with below:

enter image description here

Note that, key value will be visible only for few seconds after secret's creation and you cannot retrieve it later.

If that's the case, delete the existing secret and create new secret to copy it's value like below:

enter image description here

I have adjusted the appsettings.json file by replacing Client Secret value as below:

enter image description here

When I ran the sample now, I got the claims of signed in user including roles like below:

enter image description here

So, try changing Client Secret with key value in your appsettings.json file .

  • Related