0

I implemented a JWT token authentication where the register and login are working fine.

This is what I normally do with inbuilt authentication

var currentUser = await _userManager.GetUserAsync(HttpContext.User); 
var category = _context.Categories.Where(m=>m.ApplicationUserId == currentUser.Id); 
return View(await category.ToListAsync());

I will get the current logged in user from the httpContext, then match the current user id(that is the application user Id of the current user) with the application userid and return the matching list.

if the condition did not match then do something else.

I cant seem to get this with JWT authentication. I am using blazor client

I have tried different approach but still not getting it. I thought I could get the current user like this but I the application user Id. I was getting the username of the current user.

        internal async Task<List<Staff>> GetAllStaffServices()
        {
            var currentUser = httpContextAccessor.HttpContext.User.Identity.Name.ToString();
            var another = userManager.FindByNameAsync(currentUser);

            //var userId = this.User.FindFirst(ClaimTypes.NameIdentifier).Value;
            var staff = applicationDbContext.Staffs.Where(m => m.ApplicationUserId == another.Id);

            return await staffs.ToListAsync();
        }

This is my login method

    public async Task<IActionResult> Login([FromBody] LoginModel login)
        {
            var result = await _signInManager.PasswordSignInAsync(login.UserName, login.Password, false, false);

            if (!result.Succeeded) return BadRequest(new LoginResult { Successful = false, Error = "Username and password are invalid." });

            var user = await _signInManager.UserManager.FindByNameAsync(login.UserName);
            var roles = await _signInManager.UserManager.GetRolesAsync(user);

            var claims = new List<Claim>();


            claims.Add(new Claim(ClaimTypes.Name, login.UserName));
            claims.Add(new Claim(JwtRegisteredClaimNames.Jti, user.Id));
            claims.Add(new Claim(JwtRegisteredClaimNames.Email, user.Email));

            foreach (var role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expiry = DateTime.Now.AddDays(Convert.ToInt32(_configuration["JwtExpiryInDays"]));

            var token = new JwtSecurityToken(
                _configuration["JwtIssuer"],
                _configuration["JwtAudience"],
                claims,
                expires: expiry,
                signingCredentials: creds
            );

            return Ok(new LoginResult { Successful = true, Token = new JwtSecurityTokenHandler().WriteToken(token) });
        }
    }

config services

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

            services.AddDefaultIdentity<RegisterInfoModel>().AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                    {
                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuer = true,
                            ValidateAudience = true,
                            ValidateLifetime = true,
                            ValidateIssuerSigningKey = true,
                            ValidIssuer = Configuration["JwtIssuer"],
                            ValidAudience = Configuration["JwtAudience"],
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
                        };
                    });
            services.AddScoped<StaffServices>();
            services.AddMvc().AddNewtonsoftJson();
            services.AddResponseCompression(opts =>
            {
                opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                    new[] { "application/octet-stream" });
            });
        }

techstack
  • 1,367
  • 4
  • 30
  • 61
  • When you call `HttpContext.User.Claims` in the web API's controller method it should contain all the claims you put in the JWT token, as long as everything is configured well. – Dennis VW Jan 04 '20 at 07:39
  • How to you mean configure well? in the startup class? Should I post my config ? – techstack Jan 04 '20 at 08:01
  • I have tried this. I only got the username and the role. not other properties like userId, PhoneNumber, Email etc – techstack Jan 04 '20 at 08:13
  • The issue at hand is that the identity system for ASP.NET Core relies on the `ClaimTypes.NameIdentifier` constant for the user ID, however, the claim that corresponds to user ID on an OpenID JWT token is simply `Jti`. You can turn off this behavior by adding `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();` to the ConfigureServices so that it clears its internal list of ClaimTypes and leaves it be. – Dennis VW Jan 04 '20 at 08:43
  • @techstack, the heart of your question is in these lines: I have tried different approach but still not getting it. I thought I could get the current user like this but I the application user Id. I was getting the username of the current user. These lines are not only vague, but the grammar mistakes make it harder to understand your issue. Please be clear about your issue... – enet Jan 04 '20 at 09:13
  • 1
    @enet I want to get the current logged in userid. with that I can get the list of Item that has the current user Id. – techstack Jan 04 '20 at 09:19
  • techstack, do you parse the Jwt token in the client into json, and passed it in the AuthenticationHeaderValue, something like this: @code { StaffServices[] services; protected override async Task OnInitializedAsync() { var token = await TokenProvider.GetTokenAsync(); services = await Http.GetJsonAsync( "api/services", new AuthenticationHeaderValue("Bearer", token)); } } – enet Jan 04 '20 at 11:07

1 Answers1

0
  • You shouldn't add user.Id as a claim. What for ?
  • You shouldn't add user.Email as a claim. What for ? UserName == Email
  • Now you can, in your Web Api end point annotated by Authorize attribute, code something like this:

    var userName = HttpContext.User.Identity.Name.ToString();
    var user= userManager.FindByNameAsync(userName);
    
    var staff = applicationDbContext.Staffs.Where(m => m.ApplicationUser.Id == 
    user.Id);
    
    return await staffs.ToListAsync();
    

Note: No need to use HttpContextAccessor... HttpContext is available in the controller

The code here is very similar to yours. It extricates the user name from the HttpContext, call the FindByNameAsync to retrieve the user, and then use it.

Please, run it and report if its OK. If not, report the issue.

Update: If you wish to add the user Id to the Jwt token, in addition to the user name, you should do the following in your Login method:

claims.Add(new Claim(ClaimTypes.Name, login.UserName));
 claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id));

And to get the user id from the GetAllStaffServices method, you can call UserManager.GetUserId, passing it a claims principal object extracted from HttpContext:

var userId = UserManager.GetUserId (HttpContext.User);
enet
  • 41,195
  • 5
  • 76
  • 113
  • I was expecting a userid like this `248ea975-a933-4258-adc4-01bd8bd2865a` which is in `dbo.AspNetUsers` but when I debug and set break point I got Id=51. This will obviously throw an error that `operator == cannot be applied to operand int and string` – techstack Jan 04 '20 at 11:10
  • Do you mean that, following my code above, user.Id == 51 ? I don't think this is likely. Did you try my code ? – enet Jan 04 '20 at 12:15
  • Ok, let me try this – techstack Jan 04 '20 at 13:13
  • Thanks @enet. I appreciate. Its working now. and everyone. thanks for your contribution – techstack Jan 04 '20 at 13:50
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205347/discussion-between-techstack-and-enet). – techstack Jan 04 '20 at 15:03
  • One of the reason to include uid in the claim is when the user can create something and the database needs to save the creator id. Retrieving the uid from claim is convenient and faster than exchanging the uid from some data store using the user name. – Nico Sep 19 '21 at 00:51
  • This `HttpContext.User.Identity.Name.ToString();` is null for me. Why is that? Where as the following has got the value from inside the claim `HttpContext.User.FindFirst(ClaimTypes.Email)?.Value;` – variable Jan 05 '22 at 17:57