2

I feel like I've read every post on Stack Overflow and nothing is matching my scenario. I've got an ASP.NET Core website that uses Microsoft Identity Platform for authentication. This is set up by calling:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(options => ...)

Then, I'm using role-based claims to authorize who is allowed to access the website. When the user authenticates, I populate their roles with the security groups they are a member of (using Graph to look up group membership).

Here is the complete block for authentication:

// Setup Graph and authentication
private static void ConfigureAuthentication(
   IServiceCollection services,
   ConfigurationManager configuration)
{
   var initialScopes =
      configuration["DownstreamApi:Scopes"]?.Split(' ') ??
      configuration["MicrosoftGraph:Scopes"]?.Split(' ');

   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
     .AddMicrosoftIdentityWebApp(options =>
     {
         configuration.Bind("AzureAd", options);
         options.TokenValidationParameters.RoleClaimType = "groups";
         options.AccessDeniedPath = "/Error/AccessDenied";
         options.ErrorPath = "/Error/Error";

         options.Events.OnTokenValidated = async context =>
         {
            if(context != null)
            {
               var allGroupIds = GetConfigurationGroups(configuration);
               await ClaimHelpers.PopulateGroupClaims(context, allGroupIds);
            }
         };
      })
      .EnableTokenAcquisitionToCallDownstreamApi(options =>
         configuration.Bind("AzureAd", options), initialScopes)
      .AddMicrosoftGraph(configuration.GetSection("MicrosoftGraph"))
      .AddInMemoryTokenCaches();

   services.AddControllersWithViews().AddMicrosoftIdentityUI();
}

// Populate the authorization policies and set the default.
private static void ConfigureGroupAuthorization(
   IServiceCollection services,
   ConfigurationManager configuration)
{
   services.AddAuthorization(p =>
   {
      p.AddPolicy(PolicyNames.PortalUser, policy =>
      {
         policy.RequireAuthenticatedUser();
         policy.RequireRole(configuration["Groups:PortalUserGroupID"]!);
      });
   });

   services.AddAuthorization(options =>
   {
      options.FallbackPolicy = options.GetPolicy(PolicyNames.PortalUser);
      options.DefaultPolicy = options.GetPolicy(PolicyNames.PortalUser)!;
   });
}

Everything works fine except when someone does not have the correct claim. Then, they are redirected to https://localhost:44321/MicrosoftIdentity/Account/AccessDenied?ReturnUrl=%2F.

This is not what I want. I want unauthorized users to be directed my custom /Error/AccessDenied action. I don't want them directed to the default Microsoft Identity callback.

I need to tell my users which security group they need to join if they can't access the page. What do I need to change or configure so users are directed to /Error/AccessDenied instead of Microsoft Identity? Do I need to change something in my Azure App Registration?

If any of my terminology is wrong, please correct it. I'm still learning ASP.NET Core.

I've read many similar posts.

This is probably the most similar post but doesn't achieve the redirect: How to override default Identity AccessDenied route in ASP.NET CORE MVC

Checked with our senior engineer and he's unfamiliar with the problem. Tried asking ChatGPT.

2 Answers 2

3

AddMicrosoftIdentityWebApp is actually using cookie scheme, you chould configure access deny path as following...

...
services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.AccessDeniedPath = new PathString("/Error/AccessDenied"); 
});
...
Sign up to request clarification or add additional context in comments.

Comments

1

.AddMicrosoftIdentityUI(); overwrites the AccessDeniedPath to "/MicrosoftIdentity/Account/AccessDenied".

You can modify after .AddMicrosoftIdentityUI call:

builder.Services.ConfigureAll(delegate (CookieAuthenticationOptions options)
{
    options.AccessDeniedPath = new PathString("/Account/AccessDenied");
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.