web-dev-qa-db-fra.com

JwtSecurityToken n'expire pas quand il le devrait

J'utilise actuellement la classe JwtSecurityToken dans l'espace de noms System.IdentityModels.Tokens. Je crée un jeton en utilisant les éléments suivants:

DateTime expires = DateTime.UtcNow.AddSeconds(10);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var genericIdentity = new System.Security.Principal.GenericIdentity(username, "TokenAuth");

ClaimsIdentity identity = new ClaimsIdentity(claims);
string secret = ConfigurationManager.AppSettings["jwtSecret"].ToString();
var securityKey = new     InMemorySymmetricSecurityKey(Encoding.Default.GetBytes(secret));
var signingCreds = new SigningCredentials(securityKey,     SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.HmacSha256Signature);
var securityToken = handler.CreateToken(
    issuer: issuer,
    audience: ConfigurationManager.AppSettings["UiUrl"].ToString(),
    signingCredentials: signingCreds,
    subject: identity,
    expires: expires,
    notBefore: DateTime.UtcNow
);
return handler.WriteToken(securityToken); 

Pour une raison quelconque, même si l'expiration est définie sur 10 secondes après l'heure actuelle, elle ne lève pas d'exception lorsque le jeton est en cours de validation avant environ 5 minutes. Après avoir vu cela, je me suis dit qu'il y avait peut-être un délai d'expiration minimal de 5 minutes. J'ai donc fixé le délai d'expiration à:

DateTime.UtcNow.AddMinutes(5);

Il expire ensuite au bout de 10 minutes, mais le message d'exception indique que le délai d'expiration est défini sur ce qu'il est censé être (5 minutes après la connexion de l'utilisateur) et qu'il affiche l'heure actuelle dans l'exception, 5 minutes après. l'heure d'expiration. Donc, il semble savoir quand il DEVRAIT expirer, mais il ne lève pas l'exception jusqu'à 5 minutes après l'heure d'expiration. Ensuite, comme le jeton semble ajouter 5 minutes à l'heure à laquelle il a expiré, je règle le délai d'expiration à:

DateTime.UtcNow.AddMinutes(-5).AddSecond(10);

J'ai testé cela et jusqu'à présent, il n'a toujours pas expiré (après plus de dix minutes). Quelqu'un peut-il s'il vous plaît expliquer pourquoi cela se produit et ce que je fais mal? De plus, si vous voyez autre chose avec le code que j'ai fourni, tout conseil serait apprécié, car je suis novice dans l'utilisation de JWT et de cette bibliothèque.

Merci d'avance

12
tkd_aj

Après avoir lu la réponse de @Denis Kucherov, j'ai découvert que je pouvais utiliser le même validateur personnalisé qu'il avait publié sans utiliser la classe JwtBearerOptions qui m'aurait obligé à ajouter une nouvelle bibliothèque.

De plus, comme il y a deux espaces de noms qui contiennent beaucoup de ces mêmes classes, je ferai en sorte de mentionner que tous utilisent les espaces de noms System.IdentityModels .... (PAS Microsoft.IdentityModels ...)

Voici le code que j'ai fini par utiliser:

private bool CustomLifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken tokenToValidate, TokenValidationParameters @param)
{
    if (expires != null)
    {
        return expires > DateTime.UtcNow;
    }
    return false;
}
private JwtSecurityToken ValidateJwtToken(string tokenString)
{
   string secret = ConfigurationManager.AppSettings["jwtSecret"].ToString();
   var securityKey = new InMemorySymmetricSecurityKey(Encoding.Default.GetBytes(secret));
   JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
   TokenValidationParameters validation = new TokenValidationParameters()
   {
       ValidAudience = "MyAudience",
       ValidIssuer = "MyIssuer",
       ValidateIssuer = true,
       ValidateLifetime = true,
       LifetimeValidator = CustomLifetimeValidator,
       RequireExpirationTime = true,
       IssuerSigningKey = securityKey,
       ValidateIssuerSigningKey = true,
   };
   SecurityToken token;
   ClaimsPrincipal principal = handler.ValidateToken(tokenString, validation, out token);
   return (JwtSecurityToken)token;
}
4
tkd_aj

Le problème est lié ClockSkew. Normalement, les bibliothèques de validation (au moins celle de MS) compensent le décalage d'horloge. ClockSkew valeur par défaut est 5 minutes. Voir une réponse ici

Vous pouvez changer ClockSkew en TokenValidationParameters:

var tokenValidationParameters = new TokenValidationParameters
{
    //...your setting

    // set ClockSkew is zero
    ClockSkew = TimeSpan.Zero
};

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = tokenValidationParameters
});

Bonne codage!

23
hien

Il semble y avoir un problème avec LifeTimeValidator. Vous pouvez simplement remplacer sa logique par un délégué personnalisé. Utilisez également la classe JwtBearerOptions pour contrôler le comportement du middleware d'authentification. Par exemple:

new JwtBearerOptions
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                ValidIssuer = _configuration["Tokens:Issuer"],
                ValidAudience = _configuration["Tokens:Audience"],
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                LifetimeValidator = LifetimeValidator,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"]))
            }
        }

Et assignez le délégué LifetimeValidotor, pour fournir sa propre logique de validation du délai d'attente:

private bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken token, TokenValidationParameters @params)
    {
        if (expires != null)
        {
            return expires > DateTime.UtcNow;
        }
        return false;
    }
6
Denis Kucherov

Je viens juste de mettre en place un middleware de jeton JWT aussi et bien que les exemples sur Internet utilisent UtcNow, je devais utiliser Now ou le délai d'expiration est écoulé. Quand j'utilise Now, l'expiration est parfaite. 

0
SledgeHammer