One of our systems integrates with Apple Pay and a JWT token is used to authenticate server-to-server requests from us to their backends. Someone noticed that a small amount of requests fail. It was usually less than 5% of failed requests, raising to 30% occasionally for short period of times.
The code was audited and we've found that someone just wrote:
private string GetToken()
{
var now = DateTime.UtcNow;
var expiry = now.AddSeconds(this.Settings.MaxTokenAge);
ECDsaSecurityKey eCDsaSecurityKey = GetEcdsaSecuritKey();
var handler = new JsonWebTokenHandler();
string jwt = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = this.Settings.IssuerId,
Audience = this.Settings.AppstoreAudience,
NotBefore = now,
Expires = expiry,
IssuedAt = now,
Claims = new Dictionary<string, object>
{
...
},
SigningCredentials = ...
});
return jwt;
}
Looks great.
Problem is, it does not always work.
What we've found out is that when there's a subtle, small difference of current time between your servers and their servers, your DateTime.UtcNow is their future. And they (correctly) reject tokens from the future.
What was applied there? Well, just:
private string GetToken()
{
var now = DateTime.UtcNow.AddMinutes(-1);
var expiry = now.AddSeconds(this.Settings.MaxTokenAge);
The result? 0% of failed requests.
Lesson learned.