Jwt on .Net Core 2.0

.net core 2.0 JWT token

How to keep the JWT token valid until the user is logged-In in the app
without ask him/her again the login credentials to give him/her
another valid JWT token?

You can set a token expiry date and keep track of that.

How to make the JWT token invalid after the user decide to logout from the app?

  • If you keep the expiry time short and keep refreshing the token expiry until the user logs out.
  • You can save some kind of blacklist of invalid tokens so you can validate against that.

Update:

The JWT consists of the Header, Payload and Signature. You can read all about it here In the payload you can set an claim called: "exp".

The docs:
The "exp" (expiration time) claim identifies the expiration time on
or after which the JWT MUST NOT be accepted for processing. The
processing of the "exp" claim requires that the current date/time
MUST be before the expiration date/time listed in the "exp" claim.

Also, while researching to clarify my answer I found this SO answer: JSON Web Token expiration.

ASP.Net Core 2.0 mixed authentication of JWT and Windows Authentication doesn't accept credentials

You need to ensure, that you NOT setting Authorization: Bearer <JWT_token> HTTP header when you trying to use Windows Auth. The key point here is how "Windows Auth" actually works. Let's look how it works with browser for example.

Let's call this "a normal flow":

  1. You navigate to http://example.com/api/resource in your browser;
  2. Your browser send a HTTP GET request to http://example.com/api/resource without any Authorization HTTP Header for now (an anonymous request);
  3. Web server (or WebAPI themself) recieve a request, find out, that there is no Authorization header and respond with 401 Not Authorized status code with WWW-Authenticate: NTLM,Negotiate HTTP Header setted up ("Go away, no anonymous access. Only 'NTLM' or 'Negotiate' guys are welcome!");
  4. Browser receive a 401 response, find out that request was anonymous, looks to WWW-Authenticate header and instantly repeat request, now with Authorization: NTLM <NTLM_token> HTTP Header ("Ok, take it easy, mr. Web server! Here is my NTLM token.");
  5. Server receive a second request, find NTLM token in Authorization header, verify it and execute request ("Ok, you may pass. Here is your resource.").

Things goes a little different, when you initialy set Authorization header to some value:

  1. Your JS require http://example.com/api/resource with JWT authorization;
  2. Your browser send a HTTP GET request to http://example.com/api/resource with Authorization: Bearer <JWT_token> HTTP Header now;
  3. Web server (or WebAPI themself) recieve a request, find out, that there is Authorization header with "Bearer" authentication scheme and again respond with 401 Not Authorized status code with WWW-Authenticate: NTLM,Negotiate HTTP Header setted up ("Go away, we don't know who are this 'Bearer' guys, but we don't like them. Only 'NTLM' or 'Negotiate' guys are welcome!");
  4. Browser receive a 401 response, find out that request was authorized and decide that this token is bad. But, as you actually set Authorization header, this means that you actually have some credentials. And so it ask you for this credentials with this dialog.

JWT on .NET Core 2.0

Here is a full working minimal sample with a controller. I hope you can check it using Postman or JavaScript call.

  1. appsettings.json, appsettings.Development.json. Add a section. Note, Key should be rather long and Issuer is an address of the service:

    ...
    ,"Tokens": {
    "Key": "Rather_very_long_key",
    "Issuer": "http://localhost:56268/"
    }
    ...

    !!! In real project, don't keep Key in appsettings.json file. It should be kept in Environment variable and take it like this:

    Environment.GetEnvironmentVariable("JWT_KEY");

UPDATE: Seeing how .net core settings work, you don't need to take it exactly from Environment. You may use setting. However,instead we may write this variable to environment variables in production, then our code will prefer environment variables instead of configuration.


  1. AuthRequest.cs : Dto keeping values for passing login and password:

    public class AuthRequest
    {
    public string UserName { get; set; }
    public string Password { get; set; }
    }
  2. Startup.cs in Configure() method BEFORE app.UseMvc() :

    app.UseAuthentication();
  3. Startup.cs in ConfigureServices() :

    services.AddAuthentication()
    .AddJwtBearer(cfg =>
    {
    cfg.RequireHttpsMetadata = false;
    cfg.SaveToken = true;

    cfg.TokenValidationParameters = new TokenValidationParameters()
    {
    ValidIssuer = Configuration["Tokens:Issuer"],
    ValidAudience = Configuration["Tokens:Issuer"],
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
    };

    });
  4. Add a controller:

        [Route("api/[controller]")]
    public class TokenController : Controller
    {
    private readonly IConfiguration _config;
    private readonly IUserManager _userManager;

    public TokenController(IConfiguration configuration, IUserManager userManager)
    {
    _config = configuration;
    _userManager = userManager;
    }

    [HttpPost("")]
    [AllowAnonymous]
    public IActionResult Login([FromBody] AuthRequest authUserRequest)
    {
    var user = _userManager.FindByEmail(model.UserName);

    if (user != null)
    {
    var checkPwd = _signInManager.CheckPasswordSignIn(user, model.authUserRequest);
    if (checkPwd)
    {
    var claims = new[]
    {
    new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
    new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()),
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(_config["Tokens:Issuer"],
    _config["Tokens:Issuer"],
    claims,
    expires: DateTime.Now.AddMinutes(30),
    signingCredentials: creds);

    return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
    }
    }

    return BadRequest("Could not create token");
    }}

That's all folks! Cheers!

UPDATE: People ask how get Current User. Todo:

  1. In Startup.cs in ConfigureServices() add

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  2. In a controller add to constructor:

    private readonly int _currentUser;
    public MyController(IHttpContextAccessor httpContextAccessor)
    {
    _currentUser = httpContextAccessor.CurrentUser();
    }
  3. Add somewhere an extension and use it in your Controller (using ....)

    public static class IHttpContextAccessorExtension
    {
    public static int CurrentUser(this IHttpContextAccessor httpContextAccessor)
    {
    var stringId = httpContextAccessor?.HttpContext?.User?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
    int.TryParse(stringId ?? "0", out int userId);

    return userId;
    }
    }

How do you implement JWT authentication in .NET Core 2.0?

in Startup.cs -> ConfigureServices, add in the following, updating with your settings:

services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Audience = "your audience";
options.ClaimsIssuer = "claims issuer";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "claims issuer",
IssuerSigningKey = { your signing key }
};
});

Next up, in Configure:

app.UseAuthentication();
app.UseMvc();

You have to make sure that UseAuthentication is BEFORE UseMvc for it to work.

Basically everything that was previously in the Configure method has now been moved to the Services section :)

Check out this blog post for an example, as well as a link to a repo where you can get the example project: Shawn Wildermuth's Blog

Core 2.0 API Auth with JWT returns unauthorized

I used JWT authentication in my one of project. I would like to show my implementation, maybe this will help you. But probably you forget to add UseAuthentication(); into configure method in startup class.

startup.cs

    public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}

public void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("AppSettings");

services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = appSettings["JwtAudience"],
ValidateIssuer = true,
ValidIssuer = appSettings["JwtIssuer"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings["JwtSigningKey"]))
};
});
}

generateToken method

    private string GenerateToken(string email)
{
SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Value.JwtSigningKey));

var token = new JwtSecurityToken(
issuer: _appSettings.Value.JwtIssuer,
audience: _appSettings.Value.JwtAudience,
claims: new[]
{
new Claim(JwtRegisteredClaimNames.UniqueName, email),
new Claim(JwtRegisteredClaimNames.Email, email),
new Claim(JwtRegisteredClaimNames.NameId, Guid.NewGuid().ToString())
},
expires: DateTime.Now.AddMinutes(_appSettings.Value.JwtExpireMinute),
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
);

return new JwtSecurityTokenHandler().WriteToken(token);
}


Related Topics



Leave a reply



Submit