Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How use a JWT token to retrieve current user data in .NET Core API?

I have implemented JWT tokens to know who the current user is in an API application that is being used by an MVC controller. I'm building something like a forum app. The user must be logged in to be able to post, so I'm basically trying to use the JWT token to store the current user's email. When the user click on "Create Post" the action should get the token and its value, the problem is that I don't know how to use the token to protect controllers or retrieve data from current user, I have already copied and pasted the token in jwt.io to check if the data is stored correctly in the token and the value (the user's email) is stored correctly.

The API controller with the "login" action:

    public async Task<IActionResult> login([FromBody] Usuario model)
    {
                //check if user exists and the password is correct

                //generates the token
                    var SecretKey = config.GetValue<string>("SecretKey");
                    var key = Encoding.ASCII.GetBytes(SecretKey);

                    var claims = new ClaimsIdentity(new Claim[] 
                    {
                        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                        new Claim(ClaimTypes.Name, user.Mail)
                    });
                    claims.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Mail));

                    var tokenDesc = new SecurityTokenDescriptor
                    {
                        Subject = claims,
                        Expires = DateTime.UtcNow.AddMinutes(20),
                        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                    };

                    var tokenHandler = new JwtSecurityTokenHandler();
                    var createdToken = tokenHandler.CreateToken(tokenDesc);

                    string bearer_token = tokenHandler.WriteToken(createdToken);

                    using(var client = new HttpClient())
                    {
                        client.DefaultRequestHeaders.Add("Authorization", "Bearer" + bearer_token);
                    }

                    return Ok(bearer_token);
                }
    }

The MVC controller from where the API is used:

    public async Task<IActionResult> login(Usuario model)
    {
            HttpClient hc = new HttpClient();
            hc.BaseAddress = new Uri("https://localhost:44325/api/Usuarios/");

            var login = await hc.PostAsJsonAsync<Usuario>("login", model);

            //check the response

            var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
            identity.AddClaim(new Claim(ClaimTypes.Name, model.Email));

            var principal = new ClaimsPrincipal(identity);

            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

            HttpContext.Session.SetString("JWToken", login.ToString());

            hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", login.ToString());

            return RedirectToAction("IndexForumList", "ForumControllerMVC");
        }
    }

This is the API method to "Create Posts" and where the token should be used, here the userId is null:

    public async Task<IActionResult> createPost([FromForm]ForumModel model)
    {
        string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        //retrieves the current user email, validates and save the content to database
        
    }

And this is the MVC method to "Create Posts":

public async Task<IActionResult> createPost(ForumModel model)
    {
        HttpClient hc = new HttpClient();
        hc.BaseAddress = new Uri("https://localhost:44325/api/Usuarios/");

        //var userPost = hc.PostAsJsonAsync<ForumModel>("Usuarios/createPost", model);

        var userPost = await hc.PostAsync("createPost", formContent);

        if(userPost.IsSuccessStatusCode == true)
        {
            return RedirectToAction("IndexForumList", "ForoControllerMVC");
        }
    }

I have been suffering with this due to my lack of knowledge about JWT, any help is appreciated.

UPDATE

The startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        var key = Encoding.ASCII.GetBytes(Configuration.GetValue<string>("SecretKey"));

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });
        services.AddSession(
            options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(10);
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });
    }
like image 503
Daniel Avatar asked Oct 22 '25 23:10

Daniel


1 Answers

If I understand your question correctly, To protect your API you can decorate it with [Authorize] attribute. Eg -

    [Authorize]
    [HttpGet]
    public IActionResult GetAll()
    {
        var users = _userService.GetAll();
        return Ok(users);
    }

And to validate your tokens since you are using .netcore for your api, you ll have to create a middleware that will validate the token before your requests hit the API endpoint. You can follow this tutorial for more details on how to use JWT with ASP.NET core.

To get user Id in your case, you ll have to validate the token first and then extract the UserId. Try changing your code in createPost api to this -

public async Task<IActionResult> createPost([FromForm]ForumModel model)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var SecretKey = config.GetValue<string>("SecretKey");
    var key = Encoding.ASCII.GetBytes(SecretKey);
    var token = HttpContext.Request.Headers["Authorization"];       
    
    tokenHandler.ValidateToken(token, new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false,
        ValidateAudience = false,
        ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

        var jwtToken = (JwtSecurityToken)validatedToken;
        var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "NameIdentifier").Value);
}

Although this should be handled in the middleware and you can then attach the authenticated user to the current HttpContext.Items collection to make it accessible within the scope of the current request. All this is explained in the tutorial in detail. Hope that helps.!

like image 56
pankaj Avatar answered Oct 24 '25 12:10

pankaj