Ertan Hasani
Ertan Hasani

Reputation: 1773

.NET Core 3.1 role-based authorization with JWT returns 403 forbidden

I am having an issue, when I am trying to use [Authorize(Roles = "Administrator")] in my controllers, it always returns 403. I am using Identity and JWT tokens. Here's my Startup.cs . I am calling both app.UseAuthentication() and app.UseAuthorization() in ConfigureServices(), but still the same, everytime it returns 403

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddIdentity<User, Role>(options =>
    {
        options.Password.RequireDigit = false;
        options.Password.RequireLowercase = false;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
        options.Password.RequiredUniqueChars = 0;
        options.Password.RequiredLength = 6;

        options.ClaimsIdentity.UserIdClaimType = "user_id";
        options.ClaimsIdentity.UserNameClaimType = "email";
        options.ClaimsIdentity.RoleClaimType = "user_role";
    })
        .AddRoles<Role>()
        .AddEntityFrameworkStores<MyDbContext>()
        .AddDefaultTokenProviders();

    var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JwtKey"].ToString());

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options => {
        options.RequireHttpsMetadata = false;
        options.SaveToken = false;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateAudience = false,
            ValidateIssuer = false,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ClockSkew = TimeSpan.Zero
        };

        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;

                return Task.CompletedTask;
            }
        };
    });

    services.AddControllers()
        .AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
            options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });
...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthentication();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Here's how I am creating token

private async Task<string> GenerateToken(DataModelUser user)
{
    var key = Encoding.UTF8.GetBytes(_appSettings.JwtKey);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new Claim[]
        {
            new Claim("user_role", user.Role.ToString()),
            new Claim("user_id", user.Id.ToString()),
            new Claim("email", user.Email)
        }),
        Expires = DateTime.UtcNow.AddDays(1),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };

    var tokenHandler = new JwtSecurityTokenHandler();
    var securityToken = tokenHandler.CreateToken(tokenDescriptor);

    return tokenHandler.WriteToken(securityToken);
}

And I am adding like this on my controller

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : ControllerBase
{
    [HttpGet]
    [Authorize(Roles = "Administrator")]
    public async Task<IActionResult> Get()
    {
        return Ok();
    }
}

Here's my JWT token payload!

{
  "user_role": "Administrator",
  "user_id": "5a6333f1-9696-4b1c-a8f8-04619ebd686d",
  "name": "Admin Admin",
  "completed_profile": "False",
  "email": "[email protected]",
  "nbf": 1597147248,
  "exp": 1597233648,
  "iat": 1597147248
}

Upvotes: 2

Views: 2627

Answers (1)

sm4llfish
sm4llfish

Reputation: 21

I had a similar issue, and I resolved it by adding RoleClaimType and NameClaimType to the TokenValidationParameter:

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuerSigningKey = true,
    ValidateAudience = false,
    ValidateIssuer = false,
    IssuerSigningKey = new SymmetricSecurityKey(key),
    ClockSkew = TimeSpan.Zero,
    RoleClaimType = IdentityModel.JwtClaimTypes.Role,
    NameClaimType = IdentityModel.JwtClaimTypes.Name
};

Upvotes: 2

Related Questions