web-dev-qa-db-ja.com

ASP.NetでのJWTトークンの例外(有効期間の検証に失敗しました。トークンに有効期限がありません。)

ASPで独自のカスタム認証を作成しています。 AzureにデプロイされたNetMobileService。 JWTトークンを使用しています。新しいトークン(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;
    }

トークンはクライアントに送信され、保存されます。しかし、トークンに基づいてメッセージを承認しようとすると、次のエラーが発生します。

生涯検証に失敗しました。トークンに有効期限がありません。

これは私がトークンを検証しようとする方法です:

    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;
        }
    }

tokenHandler.ValidateTokenの実行時に例外がスローされ、principalにnullが返されます。

私の想定では、ExpiresプロパティとIssuersプロパティを正しく設定しておらず、TokenHanlderがそれらの検証に失敗している可能性があります。ただし、jwtTokenを確認すると、すべてのクレームが正しく設定されています。

完全なデバッグ出力は次のとおりです。

JWTManager> GetPrincipal>クレーム:eメール:[email protected]

JWTManager> GetPrincipal>クレーム:nbf:1494752301

JWTManager> GetPrincipal>クレーム:exp:33051661101

JWTManager> GetPrincipal>クレーム:iat:1494752301

JWTManager> GetPrincipal>クレーム:iss:MASKED

JWTManager> GetPrincipal>クレーム:aud:MAKSED

JWTManager> GetPrincipal:IDX10225:ライフタイム検証に失敗しました。トークンに有効期限がありません。アプリケーション:トークンタイプ:

6
AHA

提案に従って ここ 、使用から切り替えることで問題を修正しました

System.IdentityModel.Tokens.Jwt.TokenHandler.CreateToken(SecurityTokenDescriptor)

new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(JwtHeader, JwtPayload)

そして、ペイロードを次のように定義しました。

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}
};

そのため、DateTimeオブジェクトがExpirsおよびIssuedAt、またはLifetimeプロパティに割り当てられることを想定しているため、SecurityTokenDescriptorクラスを使用しないことになりました(Microsoft.IdentityModel.Tokensにあるかどうかによって異なります)またはSystem.IdentityModel.Tokens名前空間)。

SecurityTokenDescriptorを使用するつもりはありません。ただし、SecurityTokenDescriptorを使用して、「exp」フィールドに正しい値を設定する方法についての解決策を見つけることができませんでした。

3
AHA

@aha、有効期限を1年先に短縮して問題を解決したようです。これは機能しますが、以前に失敗した根本的なアーキテクチャの理由を理解したい場合(そして、独自のコードで適切なアーキテクチャの変更を行う場合)、これを読むことができますSOここに投稿:- https://stackoverflow.com/a/46654832/1222775

要するに、Microsoft owinミドルウェアに対して検証されたJWTの有効期限には、上限が2147483647(これは、たまたまInt32.MaxValueでもあります)であり、これはTue, 19 Jan 2038 03:14:07 GMTに変換されます。

SOの質問では、使用した「exp」クレーム値を示す投稿したデバッグ出力は、33051661101の値でした。これは次のように変換されます:Wednesday, May 14, 3017 8:58:21 AMほぼ80年までのexp値のMicrosoftの上限:)。

マイクロソフトがこの問題をすぐに解決することを願っていますが、それまでの間、同様の問題が発生している場合は、長持ちするトークンを発行しないようにしてください。少なくとも、2038年1月19日火曜日@ 03:14を過ぎないようにしてください。 :07 GMT :)。

15
Awah Teh