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(); } }
现在我允许我的用户通过Facebook注册.一旦用户注册Facebook,我会生成一个访问令牌并将其交给他.我还应该生成刷新令牌吗? Onething来到我的脑海里,是生成一个长的访问令牌,如一天,那么这个用户必须再次使用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); } }
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; }
解决方法
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); }
现在,实际上有必要添加refresh_token.
方法将如下所示:
if (hasRegistered) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity oAuthIdentity = 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,cookieIdentity); }
现在将生成refrehs令牌.
2)在SimpleRefreshTokenProvider CreateAsync方法中使用基本context.SerializeTicket有一个问题.
Bit Of Technology的消息
Seems in the ReceiveAsync method,the context.DeserializeTicket is not
returning an Authentication Ticket at all in the external login case.
When I look at the context.Ticket property after that call it’s null.
Comparing that to the local login flow,the DeserializeTicket method
sets the context.Ticket property to an AuthenticationTicket. So the
mystery now is how come the DeserializeTicket behaves differently in
the two flows. The protected ticket string in the database is created
in the same CreateAsync method,differing only in that I call that
method manually in the GenerateLocalAccessTokenResponse,vs. the Owin
middlware calling it normally… And neither SerializeTicket or
DeserializeTicket throw an error…
因此,您需要使用Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer来对门票进行搜索和反序列化.
它将如下所示:
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));
代替:
token.ProtectedTicket = context.SerializeTicket();
而对于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)));
代替:
context.DeserializeTicket(refreshToken.ProtectedTicket);
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); }
所以..这一切!现在,在调用ExternalLogin方法后,你会得到url:
https://localhost:44301/Account/ExternalLoginCallback?access_token=ACCESS_TOKEN&token_type=bearer&expires_in=300&state=STATE&refresh_token=TICKET&returnUrl=URL
我希望这有帮助)