Role based authorization in ASP.NET Core 3.1 with Identity and ExternalLogin

Rémy Huot

Im new to .NET Core and I'm trying to setup Role based authorization in a .NET Core 3.1 project. I believe I clicked on every tutorials and threads talking about it online. My problem is that it seems to be working very easily on the tutorials, but it doesn't work for me. According to tutorials I have found, all I would have to do is assign a role to a user in a database, then use [Authorize(Roles="roleName")] before a Controller's Action. When I do that I always get a 403 error for a user having the specified role. When I use userManager.GetRolesAsync(user), I see that the user has the role. When I make a request to this action with [Authorize], it works when the user is logged in, as expected.

I checked in debug mode ClaimsPrincipal.Identity for the current user and I found out that RoleClaimType = "role". I checked the claims of the current user and found out that it doesn't have a claim with a type "role". Is this how [Authorize(Roles="...")] works? Does it look a the claims? If so, how do I had a claim for the user's role? The only way for a user to login in this application is with a Google account. So how am I supposed to add a claim if they are managed by the Google login?

Here's my code in Startup.cs

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

    services.AddDefaultIdentity<ApplicationUser>()
        .AddRoles<ApplicationRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

    services.AddAuthentication()
        .AddGoogle(options =>
        {
            IConfigurationSection googleAuthNSection =
            Configuration.GetSection("Authentication:Google");

            options.ClientId = googleAuthNSection["ClientId"];
            options.ClientSecret = googleAuthNSection["ClientSecret"];
        })
        .AddIdentityServerJwt();

    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "ClientApp/dist";
    });
}

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    if (!env.IsDevelopment())
    {
        app.UseSpaStaticFiles();
    }
    app.UseRouting();
    app.UseIdentityServer();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller}/{action=Index}/{id?}");
        endpoints.MapRazorPages();
    });

    app.UseSpa(spa =>
    {
        spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseAngularCliServer(npmScript: "start");
            }
    });
}

Here's an exemple of an Action of a Controller

[Authorize(Roles = "Admin")]
[HttpGet("userinformations")]
public async Task<UserInformations> GetCurrentUserInformations()
{
    string strUserId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);

    ApplicationUser user = await userManager.FindByIdAsync(strUserId);

    string[] roles = (await userManager.GetRolesAsync(user)).ToArray();

    UserInformations userInfo = new UserInformations()
    {
        UserName = user.UserName,
        FirstName = user.FirstName,
        LastName = user.LastName,
        Email = user.Email,
        Organization = user.idDefaultOrganisation.HasValue ? user.DefaultOrganization.OrganizationName : "",
        Claims = this.User.Claims.Select(c => $"{c.Type} : {c.Value}").ToArray(),
        Roles = roles
    };

    return userInfo;
}

When I make a request to this Action without [Authorize(Roles = "Admin")], I can see that the current user has the role Admin, but when I add it, I get a 403 error.

What am I doing wrong? I feel like I'm missing one line somewhere or something like that because it all seems so simple in the tutorials I found.

Rémy Huot

I finally found a working solution. I tried adapting @MichaelShterenberg 's code using RequireAssertion, but I couldn't get it to work because I had to query my database and I was not able to use UserManager with this solution. I ended up finding a solution based on this part of his answer :

You should probably create your own AuthorizationHandler that checks if the user is indeed Admin

I followed the answer of this thread : Dependency Injection on AuthorizationOptions Requirement in DotNet Core

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

IdentityServer4 and ASP.NET Core5.0 Identity - Role based Authorization

IdentityServer4 Role Based Authorization for Web API with ASP.NET Core Identity

ASP.NET Core 2.1 Identity: Role-based authorization -> Access Denied

IdentityServer4 Role Based Authorization for ASP.NET Core Identity = Access Denied

Asp.NET Core MVC Role based Authorization

Role Based Authorization in ASP.NET Core 6

How to implement permission based authorization in ASP.net core Identity?

Rights-based authorization with ASP.NET Core 2.1 Identity

Role authorization in .net core 2.0 using Identity

ASP.NET Identity Dynamic Role Authorization

Disable Input Fields in MVC View on Role Based Authorization using ASP.net Identity

Role based authorization using Keycloak and .NET core

Seeding asp.net core Identity Role

Role based tokens ASP.net Identity

ASP.NET Core Custom Role Based Authorization (Custom User.IsInRole)?

asp.net core 2.0 app crashes when role based authorization faild

ASP.Net Core 3.0 Windows Authentication with Custom Role Based Authorization

asp.net core 2.0 windows role based authorization always returns 403

ASP.NET Core 3.1 Web API Role based authorization not working

Asp.net core mvc role-based authorization with Azure AD

ASP.NET Core 3.1 MVC Access Denied role based authorization - Conflict with custom UserClaimsPrincipalFactory

Asp.net core role based access identity / roles make user table self referencing

Role Authorization in Asp.Net Core 3 adding roles from class properties

Claim based authorization in ASP.NET Core

Asp.NET Core Claim based Authorization

Claims based authorization in ASP.NET Core

.NET Core 3.1 role based authorization fails, getting 403 exception

Windows Authentication Asp.net core 2 database role authorization

ASP.NET Web Api with Role based authorization