Home > Software engineering >  403 Forbidden in ASP.NET Identity Role, despite having the right Role
403 Forbidden in ASP.NET Identity Role, despite having the right Role

Time:02-02

I'm building this app and so far I've implemented the Identity API.

Login/Register works, which both return a user with a token. Using [Authorize] on my controllers works when I pass the JWT token in Postman.

However, when adding [Authorize(Roles = "Administrator")], I get a 403 Response, despite checking that the role "Administrator" is indeed assigned to the user.

This is the method in the UserController:

`[Authorize(Roles = ("Administrator"))]
[HttpGet]
public async Task<ActionResult<IEnumerable<AppUser>>> GetUsers()
{
    return await _userManager.Users.ToListAsync();
}`

And this is the TokenService class:

`public class TokenService
{
    public readonly IConfiguration _config;
    public TokenService(IConfiguration config) 
    {
        _config = config;
    }
    public string CreateToken(AppUser user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email)
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["TokenKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(7),
            SigningCredentials = creds
        };

        var tokenHandler = new JwtSecurityTokenHandler();

        var token = tokenHandler.CreateToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);
    }
}`

And this is the Program.cs class `private static async Task Main(string[] args) { var builder = WebApplication.CreateBuilder(args);

    builder.Services.AddControllers(opt =>
    {
        // Every endpoint requires Authentication, except for those that we add [AllowAnonnymous] to
        var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
        opt.Filters.Add(new AuthorizeFilter(policy));
    });
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    builder.Services.AddDbContext<ApplicationDbContext>(option =>
        option.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

    builder.Services.AddControllersWithViews();

    builder.Services.AddScoped<ICompany, CompanyService>();
    builder.Services.AddScoped<IJobPosition, JobPositionService>();
    builder.Services.AddScoped<IUserService, UserService>();
    builder.Services.AddScoped<IApplicationService, ApplicationService>();
    builder.Services.AddScoped<IGetUser, UserService>();
    builder.Services.AddScoped<IDocuments, DocumentService>();
    builder.Services.AddScoped<IResults, ResultService>();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDefaultIdentity<AppUser>().AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();
    builder.Services.AddIdentityServices(builder.Configuration);
    builder.Services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireAdministratorRole",
             policy => policy.RequireRole("Administrator"));
    });

    var app = builder.Build();

    var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();

    using (var scope = scopeFactory.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<ApplicationDbContext>();
            var userManager = services.GetRequiredService<UserManager<AppUser>>();
            var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
            await context.Database.MigrateAsync();
            await Seed.SeedData(context, userManager, roleManager);
        } catch(Exception ex) {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occured during migration");
        }
    }

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();


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

    app.MapControllers();

    await app.RunAsync();
}`

What am I missing here?

CodePudding user response:

Add the roles to the claims, something like this:

public string CreateToken(AppUser user, string[] roles)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Name, user.UserName),               
            new Claim(ClaimTypes.Email, user.Email)
        };
        foreach (var userRole in roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, userRole));
        }
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["TokenKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(7),
            SigningCredentials = creds
        };

        var tokenHandler = new JwtSecurityTokenHandler();

        var token = tokenHandler.CreateToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);
    }

Also, if I remember correctly, NameIdentifier claim has to be first in order to have Identity work with signalr, in case you are planning to use it.

  • Related