Another point worth noting is that though @jps answer is actually technically correct, it really doesn't explain why you got the exception: Lifetime validation failed. The token is missing an Expiration Time. Tokentype: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'
; why would it be suggesting that you are missing an expiration time, even though you have clearly provided one as an Int64 (long)?. Theoretically any Int64 number should represent some point in time in the future, even when taking into account epcoh time constraints of starting from January 1st 1970
(https://www.rfc-editor.org/rfc/rfc7519#section-2).
The true underlying problem is the fact that the number you are using when you use Ticks is a spectacularly large number; for one, Microsoft defines it as:
the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 (0:00:00 UTC on January 1, 0001, in the Gregorian calendar. https://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx
Two things to keep in mind with the above:
- It goes all the way back to
January 1, 0001
- It is based on Nano seconds, and epoch time is based on milliseconds (a factor of 1000000).
All of that said, this should in theory still represent some point in time in the future, albeit super-far in the future.
So why is it not working -- when it really should!?
The underlying reason why you actually do end up with the exception mentioned in your SO ticket is actually NOT that you are using Ticks (instead of an epoch timestamp), but rather because Microsoft has a theoretical upper limit for validation of JWTs when using their frameworks and Owin middleware (more on that later, keep reading).
One would hope that:
Such an upper limit really shouldn't be imposed; but perhaps there are security use cases that necessitates such an imposed upper limit on the expiration date for tickets - I can't really think of one at the moment.
Bullet #1 above notwithstanding, one would hope that the said upper limit is really far out in the future (Extra credit bonus points if you are both a millennial
reading this and have heard/know of the Y2K bug?). However the limit set by the MS team is actually around the corner: January 19, 2038 @ 03:14:07 GMT
You can test it for yourself by trying to validate the following token with an exp value set for January 20th 2038 (2147558400
).
{
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "SampleToken",
"nbf": 1507578537,
"exp": 2147558400, //<== This timestamp will not work :/
"iss": "https://api.example.com",
"aud": "https://example.com"
}
It fails validation with a
Microsoft.IdentityModel.Tokens.SecurityTokenException
and an Exception Message of: IDX10225: Lifetime validation failed. The token is missing an Expiration Time
Which is NOT true, I have provided an expiration date, and it is a valid epoch timestamp (and not an outrageously large number represented by DateTime.Ticks ;).
If you repeat the same test, by trying to validate the following token with an exp value set for January 19th 2038 00:00:00 (2147472000
) -- which happens to be before the theoretical limit in the current MSFT libraries of January 19, 2038 @ 03:14:07 GMT
, it works.
{
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "SampleToken",
"nbf": 1507578537,
"exp": 2147472000,
"iss": "https://api.example.com",
"aud": "https://example.com"
}
Conclusions:
- Microsoft either needs to:
a. Extend the max date for expiration values for tokens
b. OR, add another Exception message for the cases when the exp value exceeds the current max - perhaps IDX10226: Liftetime validation not possible. Expiration date set too far in the future
.
Bonus-points
Ok, after going through such detail to point out the upper limit of the exp
claim, it will be a shame to not explain why there is an upper limit to begin with. Some reading this would have already spotted the reason for the upper limit as I have dropped clues through out the writeup; If you caught-on to it already, Kudos to you, if you haven't here is why:
Microsoft is implicitly using an integer or (Int32) when doing comparisons of the values for the exp
claim epoch timestamp for the "exp" claim. I would like to think of this as an oversight (someone probably didn't think through the architecture fully and perhaps didn't consider the implications of using (explicitly or implicitly) the Int32 vs the long data type (Int64) to store/compare epoch timestamps. It strikes me as silly that this was an actual architectural decision to choose Int32 as the data type to interact with epoch timestamps for their library -- but then again, like I said earlier in this write-up, perhaps there are security use cases that necessitates such an imposed upper limit (I just can't for the life of me think of one at the moment).
Still not convinced, here is the maximum value an Int32 can hold: 2147483647, according to Microsoft: https://msdn.microsoft.com/en-us/library/system.int32.maxvalue(v=vs.110).aspx. Assuming that 2147483647 is an epoch timestamp, guess what GMT date/time you get when convert 2147483647 into a date? Yup you do get: January 19th 2038 03:14:07 GMT. You can use https://www.epochconverter.com for conversion convenience.
Workarounds
The most obvious one is do not create tokens that expire at any point after the epoch timestamp of 2147483647. It just can't be validated by Microsoft's owin middle-ware at the current time of this writing (Hopefully they fix this soon).
Another workaround is you write your own token Lifetime validation logic. It is not as complex as one would imagine, but it does seriously help to have an understanding of the underlying Owin middle-ware, and JW* (JWT, JWE, JWS, JWK, etc. Michael B. Jones has an interesting writeup about JW* here: http://www.niso.org/apps/group_public/download.php/14003/SP_Jones_JSON_isqv26no3.pdf)