ASP.NET API OAuth2刷新令牌 - 反序列化故障单不起作用

时间:2017-12-06 04:57:39

标签: asp.net asp.net-web-api2 owin

我正在使用ASP.NET API2和OWIN实现OAuth 2刷新令牌,以下代码是我的OAuthAuthorizationOptions

 public static OAuthAuthorizationServerOptions AuthorizationServerOptions
    {
        get
        {
            if (_AuthorizationServerOptions == null)
            {
                _AuthorizationServerOptions = new OAuthAuthorizationServerOptions()
                {
                    AuthenticationType = OAuthDefaults.AuthenticationType,
                    AllowInsecureHttp = true,
                    TokenEndpointPath = new PathString(AuthSettings.TokenEndpoint),
                    AuthorizeEndpointPath = new PathString(AuthSettings.AuthorizeEndpoint),
                    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(AuthSettings.TokenExpiry),
                    Provider = new CustomOAuthAuthorizationServerProvider(AuthSettings.PublicClientId),
                    // TODO: Remove the dependency with Thinktecture.IdentityModel library here
                    AccessTokenFormat = new CustomJWTFormat(),
                    RefreshTokenProvider = new CustomRefreshTokenProvider()
                };
            }
            return _AuthorizationServerOptions;
        }
    }

这是我的CustomRefreshTokenProvider类

public override Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var identifier = context.Ticket.Identity.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
        if (identifier == null || string.IsNullOrEmpty(identifier.Value))
        {
            return Task.FromResult<object>(null);
        }
        var refreshToken = HashHelper.Hash(Guid.NewGuid().ToString("n"));
        var tokenIssued = DateTime.UtcNow;
        var tokenExpire = DateTime.UtcNow.AddSeconds(AuthSettings.RefreshTokenExpiry);
        context.Ticket.Properties.IssuedUtc = tokenIssued;
        context.Ticket.Properties.ExpiresUtc = tokenExpire;
        context.Ticket.Properties.AllowRefresh = true;
        var protectedTicket = context.SerializeTicket();
        AuthService.AddUserRefreshTokenSession(
            identifier.Value,
            refreshToken,
            tokenIssued,
            tokenExpire,
            protectedTicket);
        context.SetToken(refreshToken);
        return Task.FromResult<object>(null);
    }
    public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        var refToken = context.Token;
        var protectedTicket = AuthService.GetProtectedTicket(refToken);
        if (!string.IsNullOrEmpty(protectedTicket))
        {
            context.DeserializeTicket(protectedTicket);
        }
        return Task.FromResult<object>(null);
    }

我使用postman将POST请求发送到令牌端点,如下所示 Postman refresh token 服务器返回400错误的请求状态代码。 我调试并发现context.DeserializeTicket(protectedTicket) 抛出异常

Exception thrown: 'System.Security.Cryptography.CryptographicException' in System.Web.dll

我不认为这是到期问题,因为 AuthSettings.RefreshTokenExpiry是30天后。 我还尝试将Machine密钥添加到我的web.config OAuth Refresh Token does not deserialize / invalid_grant

但它仍无效。

有没有人有想法? 任何解决方案将受到高度赞赏。

1 个答案:

答案 0 :(得分:0)

对不起,迟到的回答。 我已经解决了这个问题并最终找到了一个解决方案,完全删除了我的项目中的Thinktecture.Identity,因为它与System.IdentityModel.Tokens.JWT.dll冲突了

另一种解决方案,如果您仍然想使用Thinktecture.Identity,请将System.IdentityModel.Tokens.JWT降级为v4.0.2.206221351(此版本适用于我,我还没有使用其他版本进行测试)。< / p>

以下是CustomJWTFormat.cs的代码



    using Microsoft.Owin.Security;
    using Microsoft.Owin.Security.OAuth;
    using MST.Service.Core.Logging;
    using System;
    using System.Security.Claims;

    namespace MST.Service.Core.Auth
    {
        public class CustomJWTFormat : ISecureDataFormat
        {
            private byte[] _SymetricKey = null;
            public CustomJWTFormat()
            {
                _SymetricKey = Convert.FromBase64String(AuthSettings.JwtTokenSecret);
            }
            /// 
            /// Create jwt token 
            /// 
            /// 
            /// Token string
            public string Protect(AuthenticationTicket data)
            {
                var tokenHandler = new System.IdentityModel.Tokens.JwtSecurityTokenHandler();

                var now = DateTime.UtcNow;
                System.IdentityModel.Tokens.JwtSecurityToken jwtSecurityToken = new System.IdentityModel.Tokens.JwtSecurityToken(
                    AuthSettings.Issuer,
                    AuthSettings.JwtAudiences,
                    data.Identity.Claims,
                    DateTime.UtcNow,
                    DateTime.UtcNow.AddMinutes(AuthSettings.TokenExpiry),
                     new System.IdentityModel.Tokens.SigningCredentials(
                        new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(_SymetricKey),
                    System.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature,
                    System.IdentityModel.Tokens.SecurityAlgorithms.Sha256Digest)
                    );
                var token = tokenHandler.WriteToken(jwtSecurityToken);
                return token;
            }

            public AuthenticationTicket Unprotect(string protectedText)
            {
                var tokenHandler = new System.IdentityModel.Tokens.JwtSecurityTokenHandler();
                var validationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
                {
                    RequireExpirationTime = true,
                    ValidateIssuer = true,
                    ValidateLifetime = true,
                    AuthenticationType = OAuthDefaults.AuthenticationType,
                    ValidIssuers = new string[] { AuthSettings.Issuer },
                    ValidAudiences = new string[] { AuthSettings.JwtAudiences },
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = false,
                    IssuerSigningKey = new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(_SymetricKey)

                };
                System.IdentityModel.Tokens.SecurityToken securityToken = null;

                ClaimsPrincipal principal = null;
                try
                {
                    principal = tokenHandler.ValidateToken(protectedText, validationParameters, out securityToken);
                    var validJwt = securityToken as System.IdentityModel.Tokens.JwtSecurityToken;

                    if (validJwt == null)
                    {
                        throw new ArgumentException("Invalid JWT");
                    }
                }
                catch (Exception ex)
                {
                    LoggerManager.AuthLog.Error($"Parse token error: {ex.ToString()}");
                    return null;
                }

                // Validation passed. Return a valid AuthenticationTicket:
                return new AuthenticationTicket(principal.Identity as ClaimsIdentity, new AuthenticationProperties());
            }
        }
    }


和JWTBearerAuthenticationOptions(如果您在同一台计算机上托管资源服务器和授权服务器)

 public static JwtBearerAuthenticationOptions JwtAuthenticationOptions
    {
        get
        {
            if (_JwtAuthenticationOptions == null)
            {
                _JwtAuthenticationOptions = new JwtBearerAuthenticationOptions()
                {
                    //AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
                    AuthenticationType = OAuthDefaults.AuthenticationType,
                    AllowedAudiences = new[] { AuthSettings.JwtAudiences },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(AuthSettings.Issuer, AuthSettings.JwtTokenSecret)
                    }
                };
            }
            return _JwtAuthenticationOptions;
        }
    }
在Startup.cs中

只是像往常一样注册中间件

app.UseJwtBearerAuthentication(AuthenticationOptions.JwtAuthenticationOptions);