如何使用外部登录提供程序创建刷新令牌? [英] How to create Refresh Token with External Login Provider?

查看:14
本文介绍了如何使用外部登录提供程序创建刷新令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在网上搜索过,但找不到解决我的问题的方法.我正在我的应用程序中实施 OAuth.我正在使用 ASP .NET Web API 2 和 Owin.场景是这样的,一旦用户请求到令牌端点,他或她将收到一个访问令牌和一个刷新令牌,以生成一个新的访问令牌.我有一个课程可以帮助我生成刷新令牌.这是:

I have searched over the web and could not find a solution to my problem. I am implementing OAuth in my app. I am using ASP .NET Web API 2, and Owin. The scenario is this, once a user request to the Token end point, he or she will receive an access token along with a refresh token to generate a new access token. I have a class the helps me to generate a refresh token. Here is it :

   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 上注册,我就会生成一个访问令牌并将其提供给他.我也应该生成刷新令牌吗?我想到的一件事是,生成一个像有一天这样的长访问令牌,然后该用户必须再次使用 facebook 登录.但是如果我不想这样做,我可以给客户端一个刷新令牌,他可以用它来刷新生成的访问令牌并获得一个新的.当有人在 Facebook 或外部注册或登录时,如何创建刷新令牌并将其附加到响应中?

now i am allowing my users to register through facebook. Once a user register with facebook, I generate an access token and give it to him. Should I generate a refresh token as well ? Onething comes to my mind, is to generate a long access token like one day, then this user has to login with facebook again. But if i do not want to do that, I can give the client, a refresh token, and he can use it to refresh the generated access token and get a new. How do I create the refresh token and attach it to the response when someone register or login with facebook or externally ?

这是我的外部注册 API

Here is my external registration 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 method to generate access token

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;
        }

推荐答案

我花了很多时间来寻找这个问题的答案.所以,我很乐意帮助你.

I spent a lot of time to find the answer to this question. So, i'm happy to help you.

1) 更改您的 ExternalLogin 方法.它通常看起来像:

1) Change your ExternalLogin method. It usually looks like:

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.方法将如下所示:

Now, actually, it is necessary to add refresh_token. Method will look like this:

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);
}

现在将生成 refrehs 令牌.

Now the refrehs token will be generated.

2) 在 SimpleRefreshTokenProvider CreateAsync 方法中使用基本 context.SerializeTicket 存在问题.来自 Bit 的消息技术

2) There is a problem to use basic context.SerializeTicket in SimpleRefreshTokenProvider CreateAsync method. Message from Bit Of Technology

似乎在ReceiveAsync方法中,context.DeserializeTicket不是在外部登录情况下完全返回身份验证票证.当我在调用之后查看 context.Ticket 属性时,它是空的.与本地登录流程相比,DeserializeTicket 方法将 context.Ticket 属性设置为 AuthenticationTicket.所以现在的谜是 DeserializeTicket 在两个流.创建数据库中受保护的票证字符串在同一个 CreateAsync 方法中,不同之处仅在于我称之为方法在 GenerateLocalAccessTokenResponse 中手动,与 Owin中间件通常调用它......并且既不是 SerializeTicket 也不是DeserializeTicket 抛出错误...

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 对票证进行序列化和反序列化.它看起来像这样:

So, you need to use Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer to searizize and deserialize ticket. It will be look like this:

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 方法:

And for ReceiveAsync method:

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.像这样:

3) Now you need to add refresh_token to ExternalLogin method response. Override AuthorizationEndpointResponse in your OAuthAuthorizationServerProvider. Something like this:

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

So.. thats all! Now, after calling ExternalLogin method, you get url: https://localhost:44301/Account/ExternalLoginCallback?access_token=ACCESS_TOKEN&token_type=bearer&expires_in=300&state=STATE&refresh_token=TICKET&returnUrl=URL

我希望这会有所帮助)

这篇关于如何使用外部登录提供程序创建刷新令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆