Search code examples
c#asp.net-coreasp.net-core-mvcazure-web-app-servicemicrosoft-identity-platform

Redirect Microsoft Identity Platform Access Denied in ASP.NET Core


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.


Solution

  • 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"); 
    });
    ...