我在网上搜索过,无法找到解决问题的方法.我正在我的应用中实施OAuth.我正在使用ASP .NET Web API 2和Owin.场景是这样的,一旦用户请求令牌端点,他或她将接收访问令牌以及刷新令牌以生成新的访问令牌.我有一个类帮助我生成一个刷新令牌.就这个 :
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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我允许我的用户通过Facebook注册.一旦用户在facebook上注册,我就会生成一个访问令牌并将其提供给他.我也应该生成刷新令牌吗?我想到的是,有一天生成一个长访问令牌,然后这个用户必须再次登录facebook.但是,如果我不想这样做,我可以给客户端一个刷新令牌,他可以使用它来刷新生成的访问令牌并获得一个新的.当有人注册或登录Facebook或外部时,如何创建刷新令牌并将其附加到响应?
这是我的外部注册API
public class AccountController : ApiController
{
[AllowAnonymous]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var accessTokenResponse = GenerateLocalAccessTokenResponse(model.UserName);
return Ok(accessTokenResponse);
}
}
Run Code Online (Sandbox Code Playgroud)
//生成访问令牌的私有方法
private JObject GenerateLocalAccessTokenResponse(string userName)
{
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
JObject tokenResponse = new JObject(
new JProperty("userName", userName),
new JProperty("access_token", accessToken),
// Here is what I need
new JProperty("resfresh_token", GetRefreshToken()),
new JProperty("token_type", "bearer"),
new JProperty("refresh_token",refreshToken),
new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
Run Code Online (Sandbox Code Playgroud)
小智 26
我花了很多时间来找到这个问题的答案.所以,我很乐意帮助你.
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);
}
Run Code Online (Sandbox Code Playgroud)
现在,实际上,有必要添加refresh_token.方法将如下所示:
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);
}
Run Code Online (Sandbox Code Playgroud)
现在将生成refrehs标记.
2)在SimpleRefreshTokenProvider CreateAsync方法中使用基本context.SerializeTicket存在问题.来自技术的消息
在ReceiveAsync方法中,context.DeserializeTicket在外部登录案例中根本没有返回身份验证票证.当我查看context.Ticket属性之后调用它为null.将其与本地登录流进行比较,DeserializeTicket方法将context.Ticket属性设置为AuthenticationTicket.所以现在的谜团是DeserializeTicket在两个流程中的表现如何不同.数据库中受保护的票证字符串是在相同的CreateAsync方法中创建的,区别仅在于我在GenerateLocalAccessTokenResponse中手动调用该方法,而Owin middlware正常调用它...而且SerializeTicket或DeserializeTicket都没有抛出错误...
因此,您需要使用Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer对票证进行searizize和反序列化.它看起来像这样:
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));
Run Code Online (Sandbox Code Playgroud)
代替:
token.ProtectedTicket = context.SerializeTicket();
Run Code Online (Sandbox Code Playgroud)
对于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)));
Run Code Online (Sandbox Code Playgroud)
代替:
context.DeserializeTicket(refreshToken.ProtectedTicket);
Run Code Online (Sandbox Code Playgroud)
3)现在需要将refresh_token添加到ExternalLogin方法响应中.覆盖OAuthAuthorizationServerProvider中的AuthorizationEndpointResponse.像这样的东西:
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);
}
Run Code Online (Sandbox Code Playgroud)
所以..这就是全部!现在,在调用ExternalLogin方法之后,你得到url: https:// localhost:44301/Account/ExternalLoginCallback?access_token = ACCESS_TOKEN&token_type = bearer&expires_in = 300&state = STATE&refresh_token = TICKET&returnUrl = URL
我希望这有帮助)
@giraffe和其他人在外面
一些评论.没有必要使用自定义 tickerserializer.
以下行:
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context =
new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat, ticket);
Run Code Online (Sandbox Code Playgroud)
Startup.OAuthOptions.AccessTokenFormat使用tokenformat:.由于我们要提供refeshtoken,因此需要将其更改为: Startup.OAuthOptions.RefreshTokenFormat
否则,如果您想获取新的accesstoken并刷新refreshtoken(grant_type = refresh_token&refresh_token = ......),则反序列化器/ unprotector将失败.因为它在解密阶段使用了错误的目的关键字.
终于找到了我的问题的解决方案.首先,如果您遇到OWIN的任何问题并且无法弄清楚出了什么问题,我建议您只需启用符号调试并对其进行调试.可以在这里找到一个很好的解释:http: //www.symbolsource.org/Public/Home/VisualStudio
我的错误只是,我在使用外部登录提供程序时计算错误的ExiresUtc.所以我的refreshtoken基本上总是过期了......
如果您正在实施刷新令牌,请查看这篇gread博客文章:http: //bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api -2- owin /
要使其与外部提供程序的刷新令牌一起使用,您必须在上下文中设置两个需要的参数("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);
您需要先获取客户端并设置上下文参数
// 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);
}
| 归档时间: |
|
| 查看次数: |
6424 次 |
| 最近记录: |