Adding roles to Identity Causes Problems in Blazor Core app, what am I doing wrong.

Posted 3 years ago by mgp
Edited 3 years ago
1

Hi all, I have a problem with Authorization that just popped up.  I need to have role based authorization so I did some searching and put things together. My app worked one day and did not the next. I'm not sure what changed, but I will paste some code if you are interested in helping me. The problem seems to be when I try to protect a page or controller using @attribute [Authorize(Roles="admin")] or [Authorize(Roles="admin")] respectively.

So I created a Blazor App, changed the Authentication to 'Individual User Accounts' and checked ASP.Net Core hosted. 

At this point Roles will not work, I believe it has to do with the way the claims token are built. Research took me to updating the Services as follows:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddIdentityServer()
                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
                    options.IdentityResources["openid"].UserClaims.Add("role");
                    options.ApiResources.Single().UserClaims.Add("role");
                });

            services.AddAuthentication()
                .AddIdentityServerJwt();

            services.AddControllersWithViews();
            services.AddRazorPages();
        }

I also added a seed method so my admin account would always be available. I added a 'seed' class and I call it from Startup.Configure. Here is the Configure method:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
            UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
                app.UseWebAssemblyDebugging();
            }
            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.UseHttpsRedirection();
            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllers();
                endpoints.MapFallbackToFile("index.html");
            });

//This is the Users and Roles Seed Class
            new UAR(Configuration,userManager,roleManager);
         }

Here is the Seed Class:

    public class UAR
   {
       IConfiguration Configuration { get; set; }

       public UAR(IConfiguration configuration, UserManager<ApplicationUser> userManger, RoleManager<IdentityRole> roleManager)
       {
           Configuration = configuration;
           SeedRole(roleManager);
           SeedUser(userManger);
       }

       private void SeedRole(RoleManager<IdentityRole> roleManager)
       {
           if(roleManager.FindByNameAsync("admin").Result == null)
           {
               IdentityRole role = new IdentityRole() { Name = "admin" };
               roleManager.CreateAsync(role).Wait();
           }
           if (roleManager.FindByNameAsync("user").Result == null)
           {
               IdentityRole role = new IdentityRole() { Name = "user" };
               roleManager.CreateAsync(role).Wait();
           }
       }

       private void SeedUser(UserManager<ApplicationUser> userManger)
       {
           string adminName = "admin@AuthTest.com"; (Yes, these credential lines are just for testing convenience)
           string pwd = "!Admin12345";
           string roleName = "admin";
           if (userManger.FindByNameAsync(adminName).Result==null)
           {
               ApplicationUser user = new ApplicationUser()
               {
                   UserName = adminName,
                   Email = adminName,
                   EmailConfirmed = true
               };
               userManger.CreateAsync(user, pwd).Wait();
               if (userManger.FindByNameAsync(adminName).Result!=null)
               {
                   userManger.AddToRoleAsync(user, roleName).Wait();
               }
           }
       }
   }

So now when I run the app I have a user that can login. And in my DB, my dbo.AspNetUserRoles has the appropriate user/role link.

I change the WeatherForecastController.cs [Authorize] to [Authorize(Roles="admin")] and I change the FetchData.razor page @attribute[Authorize] to @attribute[Authorize(Roles="admin")]

Now when I login and go to the 'Fetch Data' page I get the message 

'An unhandled error has occured.Reload' 

and Google Developer tools shows a:

 'Failed to load resource: the server responded with a status of 403() info: System.Net.Http.HttpClient.BlazorApp1.ServerAPI.ClientHandler[101]

     Received HTTP response after 4140.665ms - Forbidden

Any clues? I do not know what to change to make roles work, I suspect it is a claims failure. But it appeared to work for a while before it broke.

Thanks

 

Someone is typing...

Post a Reply

You must be logged in to add a new post.
Number of online users: 0
An error has occurred. This application may no longer respond until reloaded. Reload 🗙