0
votes

I am getting 403 Forbidden while trying to implement Role based JWT Authorization using .NET Core Web API 3.1 version. Below is how my code looks like:

    // API
[HttpGet, Route("GetAll")]
[Authorize(Roles = "Admin")]
public IEnumerable<Users> GetAllUsers(string environment) {}

// JWT Token Generation
public UserDetail GenerateToken(string userName, string password)
{
    string key = Configuration["Jwt:Key"];
    var issuer = Configuration["Jwt:Issuer"];
    var audience = Configuration["Jwt:Audience"];

    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
    IdentityModelEventSource.ShowPII = true;

    //Create a List of Claims, Keep claims name short    
    var roleClaims = GetRoleClaimsFor(user);

    //Create Security Token object by giving required parameters    
    var token = new JwtSecurityToken(issuer,
                    audience: audience,
                    claims: roleClaims,
                    expires: DateTime.Now.AddMinutes(15),
                    signingCredentials: credentials);

    //Generate JWT Token
    var userToken = new JwtSecurityTokenHandler().WriteToken(token);
}

// StartUp.cs
public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(opt =>
        {
            opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            opt.DefaultChallengeScheme = Configuration["Jwt:Policy"];
        }).AddJwtBearer(Configuration["Jwt:Policy"], options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,

                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
            };
        });

        // JWT Authorization configuration
        services.AddAuthorization(auth =>
        {
            auth.AddPolicy(Configuration["Jwt:Policy"], new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            .RequireClaim(ClaimTypes.Name, Configuration["Jwt:RequiredClaim"]).Build());
        });
    }
    
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {   
         app.UseAuthentication();
         app.UseAuthorization();
    }

private IList<Claim> GetRoleClaimsFor(UserPrincipal user)
    {
        var roleClaims = new List<Claim>();
        UserRoles = new List<string>();

        // Pull only groups where user belongs to
        PrincipalSearchResult<Principal> groups = user.GetGroups();

        // Filter user groups related to GBITs application alone
        var gbitsGroups = (from grp in groups
                           select grp);

        // Loop through gbitsGroups and assign the related role 
        foreach (GroupPrincipal gbitsGrp in gbitsGroups)
        {
            try
            {
                // Pull UserGroup related Permissions
                var roles = _userGroupPermissionRepository.GetUserGroupPermissionFor(gbitsGrp.Name);
                // Loop through UserGroupPermissions to create Claims
                foreach (var role in roles)
                {
                    var claim = new Claim(role.UserGroup.GroupName, role.Permission.PermissionName);
                    roleClaims.Add(claim);
                }
                // Load Roles collection
                IList<string> usrRoles = (from role in roles
                         select role.Permission.PermissionName).ToList();
                UserRoles.AddRange(usrRoles);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        return roleClaims;
    }

I am able to run and see all the roles as claims from Principal.Identity after validating the token. But the Roles are not working as expected.

1
Could you please share the details codes about the GetRoleClaimsFor method, it seems that you didn't add the claims role or you missed the RequiredClaim value. - Brando Zhang
Thank you Brando. I have modified the code snippet (please see at the bottom of it) with GetRoleClaimsFor method. Please let me know if you need any more details. - Balaji Telugunti
Brando. This is working now by changing the role Claims type to "ClaimsType.Role" All good - Balaji Telugunti
Yes, I also guess you may use the wrong role claims type. I suggest you could reply it as answer and mark it. So that other folks who faces the same error could find the issue more easily. - Brando Zhang

1 Answers

0
votes

Creating the claims as ClaimsType.Role instead of custom string solved the issue. Below is the refactored GetClaimsFor method

private IList<Claim> GetRoleClaimsFor(UserPrincipal user)
{
    var roleClaims = new List<Claim>();
    UserRoles = new List<string>();

    // Pull only groups where user belongs to
    PrincipalSearchResult<Principal> groups = user.GetGroups();

    // Filter user groups related to GBITs application alone
    var gbitsGroups = (from grp in groups
                       select grp);

    // Loop through gbitsGroups and assign the related role 
    foreach (GroupPrincipal gbitsGrp in gbitsGroups)
    {
        try
        {
            // Pull UserGroup related Permissions
            var roles = _userGroupPermissionRepository.GetUserGroupPermissionFor(gbitsGrp.Name);
            // Loop through UserGroupPermissions to create Claims
            foreach (var role in roles)
            {
                var claim = new Claim(ClaimsType.Role, role.Permission.PermissionName);
                roleClaims.Add(claim);
            }
            // Load Roles collection
            IList<string> usrRoles = (from role in roles
                     select role.Permission.PermissionName).ToList();
            UserRoles.AddRange(usrRoles);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
    return roleClaims;
}