我正在使用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
但它仍无效。
有没有人有想法? 任何解决方案将受到高度赞赏。
答案 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);