JWTがExpireしない
ASP.NET CoreでAPIを作るときにJWT認証を実装したところ、発行したJWTのExpire、すなわちトークンの期限、すなわちと言い直すなら最初からトークンの期限と書けと思いますが、とにかく、JWTのExpirationが思ったとおりに行かなかったのでレポートします。
実装例:クライアントにトークンを発行してあげる側
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = _config["Jwt:Issuer"],
Audience = _config["Jwt:Audience"],
Expires = DateTime.UtcNow.AddMinutes(double.Parse(_config["Jwt:ExpireMinutes"])), // たとえば1分を設定
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, useridとか),
}),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_config["Jwt:Secret"])),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
例:リクエストにくっついてきたトークンを検証する側
services.AddAuthentication(
JwtBearerDefaults.AuthenticationScheme
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = config["Jwt:Issuer"],
ValidAudience = config["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Secret"])),
ClockSkew = TimeSpan.Zero // ←これがポイント
};
});
細かい実装アプローチに差はあれど、おおまかにはオーソドックスなASP.NET CoreのJWTサポートを利用したコードかと思います。(この記事を記載してた時点で3.1を利用してます。)
さて、試してみよう
クライアントがトークンを得ずAPIを実行する
401
クライアントがトークンを得てそのトークンをAuthorization Bearer ey~~
とHttpヘッダにつける
トークン発行10秒後にAPIを実行する
200
トークン作戦成功!嬉しくなってトークン発行20秒後にもっかいAPIを実行する
200
いいぞ!勝利の余韻に浸ったため3回目はトークン発行70秒後にAPIを実行する
200 ← は?
そしてしばらく待ち、もう1回、、
401
う~ん。。
問題発生。発行されたJWTは1分後には期限切れにしてほしいから SecurityTokenDescriptor.Expire
で 1分 としたのに、これ動いてないじゃないか?いろいろ書き方を変えても結果は変わらず。
しばらく調査していると、トークン検証ロジックのほうで
options.TokenValidationParameters = new TokenValidationParameters
・・・
ClockSkew = TimeSpan.Zero // ←これがポイント
このClockSkew
プロパティ、JWTのLifeTime検証の際の時間のずれを設定するという謎のプロパティで、デフォルトは 5分 らしい。
な、、なんだってー!!
たぶん Expireを設定しない場合のJWTは5分で期限切れ になるということなんでしょうね。
これを適切に設定することで、狙いどおりに期限切れを起こすことができます。
クセがすごい!