Asp.net 如何使用外部登录提供程序创建刷新令牌?
我在网上搜索过,找不到解决问题的方法。我正在我的应用程序中实现OAuth。我正在使用ASP.NETWebAPI2和Owin。场景是这样的,一旦用户请求令牌端点,他或她将收到一个访问令牌和一个刷新令牌,以生成一个新的访问令牌。我有一个类帮助我生成刷新令牌。这是:Asp.net 如何使用外部登录提供程序创建刷新令牌?,asp.net,oauth,Asp.net,Oauth,我在网上搜索过,找不到解决问题的方法。我正在我的应用程序中实现OAuth。我正在使用ASP.NETWebAPI2和Owin。场景是这样的,一旦用户请求令牌端点,他或她将收到一个访问令牌和一个刷新令牌,以生成一个新的访问令牌。我有一个类帮助我生成刷新令牌。这是: public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider { private static ConcurrentDicti
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string> ("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(15)
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
// this method will be used to generate Access Token using the Refresh Token
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
string hashedTokenId = Helper.GetHash(context.Token);
using (AuthRepository _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null )
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
// one refresh token per user and client
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
我花了很多时间来寻找这个问题的答案。所以,我很乐意帮助你 1) 更改您的ExternalLogin方法。 它通常看起来像:
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
现在,实际上,需要添加刷新令牌。
方法将如下所示:
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
// ADD THIS PART
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context =
new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat, ticket);
await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context);
properties.Dictionary.Add("refresh_token", context.Token);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer
= new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
token.ProtectedTicket = System.Text.Encoding.Default.GetString(serializer.Serialize(context.Ticket));
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
var refreshToken = context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["refresh_token"];
if (!string.IsNullOrEmpty(refreshToken))
{
context.AdditionalResponseParameters.Add("refresh_token", refreshToken);
}
return base.AuthorizationEndpointResponse(context);
}
现在将生成refrehs令牌
2) 在SimpleRefreshTokenProvider CreateAync方法中使用basic context.SerializeTicket时出现问题。
来自
似乎在ReceiveAsync方法中,context.DeserializeTicket不是
在外部登录案例中返回身份验证票证。
当我在调用后查看context.Ticket属性时,它是null。
与本地登录流相比,DeserializeTicket方法
将context.Ticket属性设置为AuthenticationTicket。所以
现在的谜团是,为什么反序列化客户端在
这两种流动。将创建数据库中受保护的票证字符串
在相同的CreateAsync方法中,不同之处在于我调用
方法在GenerateLocalAccessTokenResponse中手动执行,而不是在Owin中执行
我们都在正常地叫它…而且都没有
DeserializeTicket引发错误
因此,您需要使用Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer对票证进行搜索和反序列化。
它将是这样的:
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
// ADD THIS PART
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context =
new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat, ticket);
await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context);
properties.Dictionary.Add("refresh_token", context.Token);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer
= new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
token.ProtectedTicket = System.Text.Encoding.Default.GetString(serializer.Serialize(context.Ticket));
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
var refreshToken = context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["refresh_token"];
if (!string.IsNullOrEmpty(refreshToken))
{
context.AdditionalResponseParameters.Add("refresh_token", refreshToken);
}
return base.AuthorizationEndpointResponse(context);
}
而不是:
token.ProtectedTicket = context.SerializeTicket();
context.DeserializeTicket(refreshToken.ProtectedTicket);
对于ReceiveAsync方法:
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer = new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
context.SetTicket(serializer.Deserialize(System.Text.Encoding.Default.GetBytes(refreshToken.ProtectedTicket)));
而不是:
token.ProtectedTicket = context.SerializeTicket();
context.DeserializeTicket(refreshToken.ProtectedTicket);
3) 现在您需要将refresh\u令牌添加到ExternalLogin方法响应中。
覆盖OAuthAuthorizationServerProvider中的AuthorizationEndpointResponse。大概是这样的:
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
// ADD THIS PART
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context =
new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat, ticket);
await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context);
properties.Dictionary.Add("refresh_token", context.Token);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer
= new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
token.ProtectedTicket = System.Text.Encoding.Default.GetString(serializer.Serialize(context.Ticket));
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
var refreshToken = context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["refresh_token"];
if (!string.IsNullOrEmpty(refreshToken))
{
context.AdditionalResponseParameters.Add("refresh_token", refreshToken);
}
return base.AuthorizationEndpointResponse(context);
}
所以。。就这些!现在,在调用ExternalLogin方法后,您将获得url:
我希望这能有所帮助)@当然还有长颈鹿和其他动物 几句话。无需使用自定义tickerserializer 以下一行:
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext上下文=
新的Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat,票证);
作为令牌格式:Startup.OAuthOptions.AccessTokenFormat
。由于我们希望提供refeshtoken,因此需要将其更改为:Startup.OAuthOptions.refreshttokenformat
否则,如果您想要获取新的accesstoken并刷新refreshtoken(grant\U type=refresh\U token&refresh\U token=…),反序列化程序/反保护程序将失败。因为它在解密阶段使用了错误的关键字。终于找到了解决我问题的方法。 首先,如果您在OWIN中遇到任何问题,并且无法找出问题所在,我建议您启用符号调试并调试它。这里可以找到一个很好的解释: 我的错误只是,在使用外部登录提供程序时,我计算了一个错误的exireutc。所以我的refreshtoken基本上总是马上过期 如果您正在实现刷新令牌,请参阅这篇gread博客文章: 要使其与外部提供者的刷新令牌一起工作,必须在上下文上设置两个所需参数(“as:clientAllowedOrigin”和“as:clientRefreshTokenLifeTime”) 所以不是 var ticket = new AuthenticationTicket(oAuthIdentity, properties); var context = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext( Request.GetOwinContext(), Startup.OAuthOptions.AccessTokenFormat, ticket); await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context); properties.Dictionary.Add("refresh_token", context.Token); var票证=新的身份验证票证(oAuthIdentity,属性); var context=new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext( Request.GetOwinContext(), Startup.OAuthOptions.AccessTokenFormat,票证); wait Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(上下文); properties.Dictionary.Add(“刷新令牌”,context.token); 您需要首先获取客户端并设置上下文参数 // retrieve client from database var client = authRepository.FindClient(client_id); // only generate refresh token if client is registered if (client != null) { var ticket = new AuthenticationTicket(oAuthIdentity, properties); var context = new AuthenticationTokenCreateContext(Request.GetOwinContext(), AuthConfig.OAuthOptions.RefreshTokenFormat, ticket); // Set this two context parameters or it won't work!! context.OwinContext.Set("as:clientAllowedOrigin", client.AllowedOrigin); context.OwinContext.Set("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString()); await AuthConfig.OAuthOptions.RefreshTokenProvider.CreateAsync(context); properties.Dictionary.Add("refresh_token", context.Token); } //从数据库检索客户端 var client=authRepository.FindClient(客户端id); //仅当客户端已注册时生成刷新令牌 如果(客户端!=null) { var票证=新的身份验证票证(oAuthIdentity,属性); var context=newauthenticationtokencreatecontext(Request.GetOwinContext(),AuthConfig.OAuthOptions.refreshttokenformat,ticket); //设置这两个上下文参数,否则它将不起作用!! context.OwinContext.Set(“as:clientAllowedOrigin”,client.AllowedOrigin); context.OwinContext.Set(“as:clientRefreshTokenLifeTime”,client.RefreshTokenLifeTime.ToString()); 等待AuthConfig.OAuthOptions.RefreshTokenProvider.CreateAsync(上下文); properties.Dictionary.Add(“刷新令牌”,context.token); }
有关于这个话题的最新消息吗?我也有同样的问题。谢谢你的努力。实际上,我在一个项目中使用了这段代码,我停止了它的工作。然而,我会尝试这个代码,一旦我再次开始工作,我会让你知道我的反馈。再次感谢你的努力,我真的很感谢你的帮助。很高兴帮助)请随意提问。首先感谢你的解决方案。但出于某种原因,它对我不起作用。当我使用外部登录提供程序时,我确实获得了刷新令牌,但是当我尝试使用它来获取新的访问令牌时,我得到了一个400错误的请求错误,错误体:{“error”:“invalid_grant”}服务器上没有异常,我只是不知道如何调试它。:-|(尝试了您的解决方案和@Wouter Crooy提出的解决方案)您可以创建一个示例解决方案,或者给我一些提示吗?Alexander,您确定收到的刷新令牌有效吗?消息{“错误”:“无效”