I am using
var payload = new JwtPayload(issuer, audience, claim identities, DateTime.Now, DateTime.Now.AddMinutes(60), DateTime.Now);
to generate auth token having 60-minute expiring time but it is expiring before the expiring time
I use the following method to build a token in my asp.net core web api service:
private string BuildToken()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Key));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
_jwtOptions.Issuer, // some issuer, e.x. you can specify your localhost
_jwtOptions.Issuer,
expires: DateTime.Now.AddMinutes(_jwtOptions.Expires), // int value
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Perhaps this helps you somehow.
Related
I can't read token claims from Bearer JWT token.
Login is working, the HTTP request comes with a valid JWT token to the backend.
Login returns the token.
Here is my code on server side:
Program.cs
builder.Services.AddAuthentication(m => {
//m.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
m.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
m.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(conf => {
conf.RequireHttpsMetadata = false;
conf.SaveToken = true;
conf.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = Configuration["JWT-Issuer"],
ValidAudience = Configuration["JWT-Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT-Key"])),
ClockSkew = TimeSpan.Zero,
};
});
When I uncomment this line //m.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; postman says unauthorized. When it is kept commented, authorization succeeds.
Token is generated here.
GenerateToken Method:
private object GenerateJwtToken(string Id, string email, ApplicationUser appUser, string appUserRole, string FirstName)
{
List<Claim> claims = null;
claims = new List<Claim> {
new Claim(JwtRegisteredClaimNames.Email,email),
new Claim(JwtRegisteredClaimNames.Jti, appUser.Id),
new Claim("Role",appUserRole),
new Claim("UserName",appUser.UserName),
new Claim("TEMP", FirstName)
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT-Key"]));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expire = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JWT-Expiry"]));
var token = new JwtSecurityToken(
issuer: _configuration["JWT-Issuer"],
audience: _configuration["JWT-Issuer"],
claims: claims,
expires: expire,
signingCredentials: cred
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
When JWT Bearer token is passed to the API call decorated with [Authorize] and tested with debugger it shows that custom claim like TEMP is not present in the User.Claims List.
Reading Claims From Token
string Email = User.Claims.SingleOrDefault(x => x.Type.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")).Value;
string FirstName= User.Claims.SingleOrDefault(x => x.Type.Equals("TEMP")).Value;
Here, the email can be read successfully but I can't read the FirstName from the token. In fact User.Claims doesn't have the FirstName claim (I mean all the custom claims leaving the Registered JWT Default ClaimTypes), it only has the default token parameters which are emailaddress, id, role etc.
What should I do?
should I need to create custom authentication scheme for this purpose?
Internally in AddJwtBearer, there are some remapping done by the Token hander, where claims are renamed, like
email -> http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
This mapping can be turned of using:
// Or set this flag to false
.AddJwtBearer(opt =>
{
...
opt.MapInboundClaims = false;
});
or setting:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
The actual mapping can be found here
However, I am not sure why the TEMP claim is not found. What claims do you actually see in the authenticated User in your controller? Some claims in some cases are also ignored.
I have a login method, which give me the access and refresh token.
[HttpGet]
[AllowAnonymous]
public IActionResult Login()
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Role, "Administrator")
};
var now = DateTime.UtcNow;
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.configuration["Key"])), SecurityAlgorithms.HmacSha256);
var accessToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: claims,
notBefore: now,
expires: now.AddMinutes(10),
signingCredentials: signingCredentials));
var refreshToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
notBefore: now,
expires: now.AddYears(2),
signingCredentials: signingCredentials));
return Ok(new JwtToken
{
AccessToken = accessToken,
RefreshToken = refreshToken
});
}
And I use the access token with Postman. In the headers:
Bearer eyJhbGciOiJIUzI1...
But after 10 minutes I can't use the API because the access token is rejected. How can I renew the access token with every request to the API (within these 10 minutes)?
You could set a variable or cookie with the expiry time of the token, then every request that is made you need to check if this expiry is in the past. If it is, you should be able to use the refresh token to get a new access token.
This will ensure you are not getting a token for each request but only when the token expires.
I am very new to .NET / C#.
I am using jwt for means of authentication. I have this object:
var token = new JwtSecurityToken(
issuer: "example.com",
audience: "example.com",
claims: claims,
expires: DateTime.Now.AddDays(TOKEN_EXPIRY_DAYS),
signingCredentials: creds
);
However, I manually add a "created" property:
token.Payload["created"] = DateTimeOffset.Now.ToUnixTimeSeconds();
All I need to do is to write a test that checks to see if the token object has an attribute called "created".
I have tried something like this:
tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(
await response.Content.ReadAsStringAsync()
);
Assert.NotEmpty(tokenResponse.token["created"]);
But this fails. Any ideas?
I need to check if my JwtSecurityToken is expired or not.
I'm using System.IdentityModel.Tokens.Jwt library.
When I create a JwtSecurityToken like this:
var token = new JwtSecurityToken(
issuer: token_issuer,
audience: token_audience,
claims: claims,
expires: DateTime.Now.AddMinutes(15),
signingCredentials: creds
);
And check its lifetime, I'm getting 2 hours after the current time.
I check the lifetime this way (only for test purposes):
var lifeTime = new JwtSecurityTokenHandler().ReadToken(token).ValidTo;
And my method for validation:
private static bool ValidateToken(string token)
{
try
{
TokenValidationParameters validationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(token_salt),
ValidAudience = token_audience,
ValidIssuer = token_issuer,
RequireExpirationTime = true
};
var lifeTime = new JwtSecurityTokenHandler().ReadToken(token).ValidTo;
ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(token_last, validationParameters, out SecurityToken validatedToken);
return true;
}
catch(Exception ex)
{
}
return false;
}
Can anyone explain what's happening or am I doing some wrong?
EDIT (for explanation purposes)
Test 1
Current time of my device: 10:06
Using expiration = DateTime.Now.AddSeconds(5);
Token's lifetime = 12:06:10 and Kind = UTC
Validation is ok.
Test 2
Current time of my device: 10:16
Using expiration = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).AddSeconds(5);
Token's lifetime = 10:16:12 and Kind = UTC
Validation fails:
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. ValidTo: '12/11/2017 10:16:12' Current time: '12/11/2017 12:18:40'.
Test 3
Using expiration = DateTime.UtcNow.AddSeconds(5);
Token's lifetime = 13:07:10 and Kind = UTC
Validation is ok.
But WHY the validation passes if when I run the validation was 13:12 and token's lifetime is 13:07? Is there a minimum time to bet set in expiration?
From the only source of this library got from google search you can see that the time is being converted to universal time (UTC).
DateTime.Now.AddMinutes(15) seems to be the problem.
Replacing it with Datetime.UtcNow.AddMinutes(15) might fix it
is your local time zone is UTC+2:00?
I know, that this is an old question, but TokenValidationParameters class has property called ClockSkew, which is set by default to 5 minutes. It might be your problem, as it was mine. Just set this property to for example to 1 second - ClockSkew = TimeSpan.FromSeconds(1).
It is not wrong time, in fact you create the expiration time based on your local time, but it looks for Utc time. so the solution is using Utc time in serverside mthod to generate expiratoin time and then in client side check the expiration token time by UtcNow.
Server :
var token = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
audience: _configuration["JWT:ValidAudience"],
expires: DateTime.UtcNow.AddMinutes(tokenValidityInMinutes),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
Client :
var identity = string.IsNullOrEmpty(tokenDTO?.Token) || tokenDTO?.Expiration < DateTime.UtcNow
There are millions of guides out there, and none of them seem to do what I need. I am creating an Authentication Server, that simply just needs to issue, and validate/reissue tokens. So I can't create a middleware class to "VALIDATE" the cookie or header. I am simply receiving a POST of the string, and I need to validate the token that way, instead of the Authorize middleware that .net core provides.
My Startup Consists of the only Token Issuer Example I could get working.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseExceptionHandler("/Home/Error");
app.UseStaticFiles();
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var options = new TokenProviderOptions
{
// The signing key must match!
Audience = "AllApplications",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
Issuer = "Authentication"
};
app.UseMiddleware<TokenProviderMiddleware>(Microsoft.Extensions.Options.Options.Create(options));
I can use the middleware on creation since I just need to intercept the body for the username and password. The middleware takes in the options from the previous Startup.cs code, checks the Request Path and will Generate the token from the context seen below.
private async Task GenerateToken(HttpContext context)
{
CredentialUser usr = new CredentialUser();
using (var bodyReader = new StreamReader(context.Request.Body))
{
string body = await bodyReader.ReadToEndAsync();
usr = JsonConvert.DeserializeObject<CredentialUser>(body);
}
///get user from Credentials put it in user variable. If null send bad request
var now = DateTime.UtcNow;
// Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, JsonConvert.SerializeObject(user)),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.ToString(), ClaimValueTypes.Integer64)
};
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
///fill response with jwt
}
This large block of code above will Deserialize the CredentialUser json and then execute a stored procedure that returns the User Object. I will then add three claims, and ship it back.
I am able to successfully generate a jwt, and using an online tool like jwt.io, I put the secret key, and the tool says it is valid, with an object that I could use
{
"sub": " {User_Object_Here} ",
"jti": "96914b3b-74e2-4a68-a248-989f7d126bb1",
"iat": "6/28/2017 4:48:15 PM",
"nbf": 1498668495,
"exp": 1498668795,
"iss": "Authentication",
"aud": "AllApplications"
}
The problem I'm having is understanding how to manually check the claims against the signature. Since this is a server that issues and validates tokens. Setting up the Authorize middleware is not an option, like most guides have. Below I am attempting to Validate the Token.
[Route("api/[controller]")]
public class ValidateController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Validate(string token)
{
var validationParameters = new TokenProviderOptions()
{
Audience = "AllMyApplications",
SigningCredentials = new
SigningCredentials("mysupersecret_secretkey!123",
SecurityAlgorithms.HmacSha256),
Issuer = "Authentication"
};
var decodedJwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
var valid = new JwtSecurityTokenHandler().ValidateToken(token, //The problem is here
/// I need to be able to pass in the .net TokenValidParameters, even though
/// I have a unique jwt that is TokenProviderOptions. I also don't know how to get my user object out of my claims
}
}
I stole borrowed this code mostly from the ASP.Net Core source code: https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs#L45
From that code I created this function:
private string Authenticate(string token) {
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
List<Exception> validationFailures = null;
SecurityToken validatedToken;
var validator = new JwtSecurityTokenHandler();
// These need to match the values used to generate the token
TokenValidationParameters validationParameters = new TokenValidationParameters();
validationParameters.ValidIssuer = "http://localhost:5000";
validationParameters.ValidAudience = "http://localhost:5000";
validationParameters.IssuerSigningKey = key;
validationParameters.ValidateIssuerSigningKey = true;
validationParameters.ValidateAudience = true;
if (validator.CanReadToken(token))
{
ClaimsPrincipal principal;
try
{
// This line throws if invalid
principal = validator.ValidateToken(token, validationParameters, out validatedToken);
// If we got here then the token is valid
if (principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
return principal.Claims.Where(c => c.Type == ClaimTypes.Email).First().Value;
}
}
catch (Exception e)
{
_logger.LogError(null, e);
}
}
return String.Empty;
}
The validationParameters need to match those in your GenerateToken function and then it should validate just fine.