I am creating my own custom authentication on ASP. Net MobileService deployed on Azure. I use JWT tokens. Here is how I generate a new token (claimType = email):
public static string GetSecurityToken(String email)
{
var symmetricKey = Convert.FromBase64String(signingKey);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, email)
}),
NotBefore = now,
Expires = now.AddYears(10),
Issuer = issuer,
Audience = audience,
IssuedAt = now,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature),
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
The token is sent to the client and stored. But when I try to authorize a message based on its token, I get the error:
Lifetime validation failed. The token is missing an Expiration Time.
This is how I try to validate the token:
public static ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(signingKey);
Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Validating Token: {0}", token));
foreach (Claim claim in jwtToken.Claims)
{
Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Claims: {0}", claim.ToString()));
}
var validationParameters = new TokenValidationParameters()
{
//RequireExpirationTime = true,
//ValidateLifetime = true,
ValidateIssuer = true,
ValidateAudience = true,
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey),
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
if (principal != null)
Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Principal: {0}", principal));
return principal;
}
catch (SecurityTokenException ex)
{
Debug.WriteLine(String.Format("JWTManager > GetPrincipal: {0}", ex.Message));
return null;
}
catch (Exception ex)
{
Debug.WriteLine(String.Format("JWTManager > GetPrincipal: {0}", ex.Message));
return null;
}
}
Exception is thrown on executing tokenHandler.ValidateToken
and null is returned to principal
.
My assumption is that maybe I am not setting the Expires
and Issuers
properties correctly and the TokenHanlder fails to validate them. However, when I check the jwtToken, all the claims are correctly set.
Here is the complete debug output:
JWTManager > GetPrincipal > Claims: email: [email protected]
JWTManager > GetPrincipal > Claims: nbf: 1494752301
JWTManager > GetPrincipal > Claims: exp: 33051661101
JWTManager > GetPrincipal > Claims: iat: 1494752301
JWTManager > GetPrincipal > Claims: iss: MASKED
JWTManager > GetPrincipal > Claims: aud: MAKSED
JWTManager > GetPrincipal: IDX10225: Lifetime validation failed. The token is missing an Expiration Time. Application: Tokentype:
@aha, it looks like you solved your problem by shortening your expiration datetime to just one year in the future. Which works, but if you want to understand the underlying architectural reason why it was failing before (and as such make appropriate architectural changes in your own code), you can read this SO post here: https://stackoverflow.com/a/46654832/1222775.
The bottom line is that expiration date for JWTs validated against the Microsoft owin middle ware have an upper limit of 2147483647
(which also happens to be Int32.MaxValue), and that translates to: Tue, 19 Jan 2038 03:14:07 GMT
In your SO question, the debug output you posted showing the "exp" claim value you used, was a value of 33051661101
which translates to: Wednesday, May 14, 3017 8:58:21 AM
which blows past Microsoft's upper limit for the exp value by almost 80 years :).
I hope Microsoft will solve this issue soon, but in the mean time for anyone experiencing a similar issue, try no to issue too long lasting tokens, or at least, don't make it go past Tue, 19 Jan 2038 @ 03:14:07 GMT :).
Following the suggestion here, I fixed the problem by switching from using
System.IdentityModel.Tokens.Jwt.TokenHandler.CreateToken(SecurityTokenDescriptor)
to
new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(JwtHeader, JwtPayload)
.
And defined the payload as follows:
DateTime centuryBegin = new DateTime(1970, 1, 1);
var exp = new TimeSpan(DateTime.Now.AddYears(1).Ticks - centuryBegin.Ticks).TotalSeconds;
var now = new TimeSpan(DateTime.Now.Ticks - centuryBegin.Ticks).TotalSeconds;
var payload = new System.IdentityModel.Tokens.Jwt.JwtPayload
{
{"iss", issuer},
{"aud", audience},
{"iat", (long)now},
{"exp", (long)exp}
};
So, I ended up not using the SecurityTokenDescriptor class because it expects DateTime objects to be assigned to Expirs
and IssuedAt
, or Lifetime
properties (depending on whether it is in the Microsoft.IdentityModel.Tokens
or System.IdentityModel.Tokens
namespace).
I have no intention of using SecurityTokenDescriptor; however, I couldn't find a solution on how to use SecurityTokenDescriptor and still set correct values to the "exp" field.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With