[SOLVED] Authorize attribute with role redirects to Access denied

Issue

I have simple ASP .NET MVC application.
I’m trying to do authentication and authorization.

For authentication I have setup Azure AD and I’m adding role to the HttpContext.User using a middleware.
I know that the role is added because on the View if I do conditional rendering with User.IsInRole("Admin") the UI is correctly rendered. But the controller with Authorize[Roles = "Admin"] redirects me to

http://localhost:5433/MicrosoftIdentity/Account/AccessDenied?ReturnUrl=%2FHome%2FAdd

I even tired Authorize[Policy = "Admin"] but the result is same.

From services if I remove AddMicrosoftIdentityUI() then it is redirected to

http://localhost:5433/Account/AccessDenied?ReturnUrl=%2FHome%2FAdd

I don’t know how does the Authorize attributes work. Does it not check User.IsInRole("Admin")? Please let me know

Also for me the roles are added in UserDetails table and I do have a role as ‘Admin’

Also I could have written a custom attribute and make it work. I just want to know what is the real issue right now.

Code:

Startup.cs

 public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextPool<EmpDBContext>(
            options => options.UseSqlServer(Configuration.GetConnectionString("EmployeeDBConnection")));

            services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd");


            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                 .RequireAuthenticatedUser()
                 .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).AddMicrosoftIdentityUI().AddNewtonsoftJson();

            services.AddAuthorization(options =>
            {
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin"));
                options.AddPolicy("Employee", policy => policy.RequireRole("Employee"));
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

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

            app.UseAddClaimsFromDb(); // CUSTOM MIDDLEWARE TO ADD ROLE TO USER

            // CHECKING IF ROLE WAS ADDED
            app.Use(async (context, next) =>
            {
                if (context.User != null && context.User.Identity.IsAuthenticated)
                {
                    bool x = context.User.IsInRole("Admin");
                    System.Console.WriteLine(x); // RETURNS TRUE

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

UseAddClaimsFromDb (AddClaimMiddleware.cs)

public class AddClaimMiddleware
    {
        private readonly RequestDelegate next;

        public AddClaimMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task InvokeAsync(HttpContext httpContext, EmpDBContext dBContext)
        {
            var user = httpContext.User;

            // does not perist between request
            if(user != null && user.Identity.IsAuthenticated)
            {
                string email = user.Identity.Name;

                UserDetail userDetail = await dBContext.UserDetails.FirstOrDefaultAsync(e => e.EmailId == email);
                string role = userDetail?.Role ?? "Employee";

                ClaimsIdentity claimsIdentity = new ClaimsIdentity();
                claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));

                user.AddIdentity(claimsIdentity);

               // httpContext.Session.SetString("employeeId", userDetail.EmployeeId.ToString());

            }

            await next(httpContext);
        }
    }

Solution

I fixed the above by rearranging the middleware sequence

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

I still don’t know the real reason why it worked. It would be great if someone can update this or add a new answer.

Answered By – Sandeep Ranjan

Answer Checked By – Willingham (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *