locked
JWT token, What to do with it RRS feed

  • Question

  • User-1771888428 posted

    I implemented Register and Login in blazor using Identity. part of the Login Api method is:

     await signInManager.SignOutAsync();
                        Microsoft.AspNetCore.Identity.SignInResult result =
                            await signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, true);
                        if (result.Succeeded)
                        {
                            user.LastLoginDate = DateTime.Now;
                            await userManager.UpdateAsync(user);
                            //return Redirect(returnUrl ?? "/");
                            model.Success = true;
                            model.Message = "Success.";
                            model.Token = _tokenService.BuildToken(model.User);
                            return model;
                        }

    When user is successfully authenticated and I receive 'LoginModel' in client page, that has a Token for the UserName (in token service I used new Claim(JwtRegisteredClaimNames.UniqueName, userName),)

    My question is now that user is authenticated and I have a token, What do I do with this token? an article says store it in browser local storage. I want to be able to use client "HttpContext"  class to access HttpContext.HttpContext.User.Identity; and HttpContext.HttpContext.User.IsInRole; to show/hide links and sections, I don't know how to do these, Please help.

    Monday, June 3, 2019 1:57 PM

Answers

  • User-1771888428 posted

    I find the solution from a post on the internet, The solution is adding roles and role claims to the token in the JwtTokenService:

    AppUser user = await userManager.FindByNameAsync(userName);
                IdentityOptions _options = new IdentityOptions();
                var claims = new List<Claim>()
                {
                    new Claim(JwtRegisteredClaimNames.Sub, userName),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
                    new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName),               
                };           
                var userClaims = await userManager.GetClaimsAsync(user);
                var userRoles = await userManager.GetRolesAsync(user);
                claims.AddRange(userClaims);
                foreach (var userRole in userRoles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, userRole));
                    var role = await roleManager.FindByNameAsync(userRole);
                    if (role != null)
                    {
                        var roleClaims = await roleManager.GetClaimsAsync(role);
                        foreach (Claim roleClaim in roleClaims)
                        {
                            claims.Add(roleClaim);
                        }
                    }
                }           

    This works fine with attributes like [Authorize(Roles="Administrators")] that I tested.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, June 9, 2019 9:27 AM

All replies

  • User-1771888428 posted

    I could make WeatherForecasts() method with an [Authorize] attribute work and get data back by sending the token to the api method:

    protected override async Task OnInitAsync()
        {
            var token = await tokenService.GetAccessToken("websiteToken");        
            Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
            forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
        }

    tokenService is a client service to store and retrieve token from local storage using JavaScript interop, Whie the method works with [Authorize] and no roles, it doesn't work when I apply [Authorize(Roles = "Users")] to api method, The user is in that role and I checked the database to confirm, So how to solve this problem?

    EDIT:

    Also I checked injected HttpContextAccessor instance in the controller for Users is not working, For example HttpContextAccessor.HttpContext.User.Identity.Name returns empty string and HttpContextAccessor.HttpContext.User.IsInRole() always returns false.

    Is JWT auth, something that is used only for a simple authentication? or there are solutions to these problems?

    Tuesday, June 4, 2019 8:06 AM
  • User475983607 posted

    The code is a bit confusing.  There is no indication what security scheme you are using.  

    The PasswordSignInAsync() method adds the token to the auth cookie and passes it back to a browser. 

    I assume this works and creates a token but it is not clear what's in the token.

    model.Token = _tokenService.BuildToken(model.User);

    If you did not tell the action to look for a bearer token then the action is probably looking for a cookie.  Or perhaps the bearer token does not have contain the role.

    Are you following a tutorial?

    Tuesday, June 4, 2019 1:02 PM
  • User-1771888428 posted

    Yes I followed this tutorial: https://medium.com/@st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d

    I use its JwtTokenService, the only change I made in this service, is to its claim so that its claim uses sub instead of email:

    var claims = new[] {
                    new Claim(JwtRegisteredClaimNames.Sub, userName),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                };

    I also send the token that I received from login page, to a secure action method that uses [Authorize] and it is fine, But when I apply [Authorize(Roles = "Users")] the action method returns forbidden, Also when I use [Authorize] and execution enters the method HttpContextAccessor.HttpContext.User.IsInRole("Users") is false and HttpContextAccessor.HttpContext.User.Identity.Name is empty. 

    Tuesday, June 4, 2019 1:17 PM
  • User475983607 posted

    mz1378

    I also send the token that I received from login page, to a secure action method that uses [Authorize] and it is fine, But when I apply [Authorize(Roles = "Users")] the action method returns forbidden, Also when I use [Authorize] and execution enters the method HttpContextAccessor.HttpContext.User.IsInRole("Users") is false and HttpContextAccessor.HttpContext.User.Identity.Name is empty.

    This symptom happens when there are two authentication schemes configured but neither is scheme is specifically assigned to a controller or action. 

    https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-2.2&tabs=aspnetcore2x

    Try adding...

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

    Tuesday, June 4, 2019 1:23 PM
  • User-1771888428 posted

    I find the solution from a post on the internet, The solution is adding roles and role claims to the token in the JwtTokenService:

    AppUser user = await userManager.FindByNameAsync(userName);
                IdentityOptions _options = new IdentityOptions();
                var claims = new List<Claim>()
                {
                    new Claim(JwtRegisteredClaimNames.Sub, userName),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
                    new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName),               
                };           
                var userClaims = await userManager.GetClaimsAsync(user);
                var userRoles = await userManager.GetRolesAsync(user);
                claims.AddRange(userClaims);
                foreach (var userRole in userRoles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, userRole));
                    var role = await roleManager.FindByNameAsync(userRole);
                    if (role != null)
                    {
                        var roleClaims = await roleManager.GetClaimsAsync(role);
                        foreach (Claim roleClaim in roleClaims)
                        {
                            claims.Add(roleClaim);
                        }
                    }
                }           

    This works fine with attributes like [Authorize(Roles="Administrators")] that I tested.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, June 9, 2019 9:27 AM