Razor Page Authorization for Roles

Michael Mastro II 51 Reputation points
2021-06-23T03:02:41.447+00:00

Good evening. I have been trying to get Authorization working with a Razor Page. I have tried multiple thing but none have worked. So, If I have AuthenticationSchemes = "ClientCookie" in the Authorize by itself, it will work, but it does not prevent a user that should not have access to the page from accessing it. If I try to use Roles, it redirects me to Account/AccessDenied?RazorPageIWasTryingToAccess. I have even tried AuthorizeAreaPage and AuthorizeAreaFolder in the startup of the razor page and am continually sent to AccessDenied for all Razor Pages that have Authorization.

So with the Conventions my startup looks as follows:

public void ConfigureServices(IServiceCollection services)  
        {  
            services.AddSession();  
            services.AddMemoryCache();  
            services.AddAuthorization(options =>  
            {  
                options.AddPolicy("LotroAdmin", policy => policy.RequireRole("LOTRO Administrator"));  
                options.AddPolicy("SiteAdmin", policy => policy.RequireRole("Site Administrator"));  
            });  
            services.AddRazorPages(options =>   
            {  
                options.Conventions.AuthorizeAreaPage("Administration", "/UserManagement", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/UserDetails", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/RoleManagement", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/RemoveRole", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/EditUser", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/EditRole", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/AllUsers", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/AllUnapprovedUsers", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/AllDeniedUsers", "SiteAdmin");  
                options.Conventions.AuthorizeAreaPage("Administration", "/AddRole", "SiteAdmin");  
                options.Conventions.AuthorizeAreaFolder("Administration", "/Lotro", "SiteAdmin");  
                options.Conventions.AuthorizeAreaFolder("Administration", "/Lotro", "LotroAdmin");  
            });  
  
            //snipped for brevity  
  
            services.AddAuthentication(options =>  
            {  
                options.DefaultAuthenticateScheme = "ClientCookie";  
                options.DefaultSignInScheme = "ClientCookie";  
                options.DefaultChallengeScheme = "LoginServer";  
            })  
                .AddCookie("ClientCookie");  
        }  
  
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
        {  
            if (env.IsDevelopment())  
            {  
                app.UseDeveloperExceptionPage();  
            }  
            else  
            {  
                app.UseExceptionHandler("/Error");  
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
                app.UseHsts();  
            }  
  
            app.UseSession();  
            app.UseHttpsRedirection();  
            app.UseStaticFiles();  
  
            app.UseRouting();  
  
            app.UseAuthentication();  
            app.UseAuthorization();  
  
            app.UseEndpoints(endpoints =>  
            {  
                endpoints.MapRazorPages();  
                endpoints.MapControllers();  
            });  
        }  

If I removed the options from the services.AddRazorPages(); It works if I place the following on the Razor Pages:

[Authorize(AuthenticationSchemes = "ClientCookie")]  

If I have the below it does not work:

 [Authorize(AuthenticationSchemes = "ClientCookie", Roles = "Site Administrator")]  

Nothing is showing as to why it is Access Denied in the debug log, Kestrel for the Web Api or Kestrel for the Razor Pages.

A page this fails on and sends me to the Access Denied is as simple as this:

[Authorize(AuthenticationSchemes = "ClientCookie", Roles = "Site Administrator")]  
    public class LotroManagementModel : PageModel  
    {  
        public void OnGet()  
        {  
        }  
    }  

This is what I get when I try to hit that page (also the Access Denied page seems broken too):
108384-image.png

Any thoughts on how to get the roles to work with the Razor Pages? It seems like the policy method does not work, or the roles do not work.

I followed guidance on the following pages:
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-3.1
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-3.1

Edit:
Here is the code from the Login Razor Page that assigns the everything to the ClientCookie.

var loginReturn = await _client.LoginPostAsync(apiVersion, dataToSend);  
      
                 if (loginReturn.SignInResult.Succeeded)  
                 {  
                     var claim = new Claim(ClaimTypes.Name, loginReturn.Principal.Name);  
                     var identity = new ClaimsIdentity(new[] { claim }, loginReturn.Principal.AuthenticationType);  
                          
                     if (loginReturn.Access_Token != null)  
                     {  
                         ReturnUrl = loginReturn.ReturnUrl;  
                         Response.Cookies.Append("Email", Input.Email, new CookieOptions { HttpOnly = true, Secure = true, IsEssential = true });  
                         Response.Cookies.Append("ClientCookiePersist", Input.RememberMe.ToString(), new CookieOptions { HttpOnly = true, Secure = true, IsEssential = true });  
                         HttpContext.Session.SetString("access_token", loginReturn.Access_Token);  
                         await HttpContext.Session.CommitAsync();  
                         // Add all the Roles and Databases here  
                         var base64Payload = loginReturn.Access_Token.Split('.')[1];  
                         var bytes = Base64UrlTextEncoder.Decode(base64Payload);  
                         var jsonPayload = Encoding.UTF8.GetString(bytes);  
                         var claims = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonPayload);  
                         foreach (var cl in claims)  
                         {  
                             identity.AddClaim(new Claim(cl.Key, cl.Value.ToString()));  
                         }  
                         var principal = new ClaimsPrincipal(identity);  
                         await HttpContext.SignInAsync(scheme: "ClientCookie", principal);  
                     }  
      
                     _logger.LogInformation("User logged in.");  
                     return LocalRedirect(ReturnUrl);  
                 }  
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,157 questions
{count} votes