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

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

问题描述

我已经搜索了网络,但没有找到一个解决我的问题。我在我的应用程序执行的OAuth。我使用ASP .NET的Web API 2,和Owin。方案是这样的,一旦一个用户请求令牌终点时,他或她将令牌沿收到访问与刷新令牌以产生新的访问令牌。我有一个类可以帮助我来生成刷新令牌。这里是:

 公共类SimpleRefreshTokenProvider:IAuthenticationTokenProvider
    {
       私有静态ConcurrentDictionary<字符串,AuthenticationTicket> _refreshTokens =新ConcurrentDictionary<字符串,AuthenticationTicket>();    公共异步任务CreateAsync(AuthenticationTokenCreateContext上下文)
        {            。变种refreshTokenId = Guid.NewGuid()的ToString(N);
            使用(AuthRepository _repo =新AuthRepository())
            {
                VAR refreshTokenLifeTime = context.OwinContext.Get<串GT; (为:clientRefreshTokenLifeTime);
                VAR令牌=新RefreshToken()
                {
                    ID = Helper.GetHash(refreshTokenId)
                    客户端Id =客户端ID,
                    主题= 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的结果=等待_repo.AddRefreshToken(标记);
                如果(结果)
                {
                    context.SetToken(refreshTokenId);
                }
            }
        }        //此方法将被用于使用刷新令牌生成访问令牌
        公共异步任务ReceiveAsync(AuthenticationTokenReceiveContext上下文)
        {            字符串hashedTokenId = Helper.GetHash(context.Token);
            使用(AuthRepository _repo =新AuthRepository())
            {
                VAR refreshToken =等待_repo.FindRefreshToken(hashedTokenId);
                如果(refreshToken!= NULL)
                {
                    //从refreshToken类protectedTicket
                    context.DeserializeTicket(refreshToken.ProtectedTicket);
                    //每个用户和客户端1更新令牌
                    VAR的结果=等待_repo.RemoveRefreshToken(hashedTokenId);
                }
            }
        }        公共无效创建(AuthenticationTokenCreateContext上下文)
        {
            抛出新NotImplementedException();
        }        公共无效接收(AuthenticationTokenReceiveContext上下文)
        {
            抛出新NotImplementedException();
        }
    }

现在我让我的用户通过Facebook注册。一旦与Facebook的用户注册,我生成一个访问令牌,并把它交给了他。我应该产生一个刷新令牌呢? Onething在我脑海中,是生成一个长的访问令牌如一日,则该用户再次与Facebook登录。但是,如果我不想这样做,我可以给客户端,刷新令牌,他可以用它来刷新生成的访问令牌,并得到一个新的。如何创建刷新令牌,并将其连接到响应当有人注册或使用Facebook登入或外部?

下面是我的外部注册API

 公共类的AccountController:ApiController
    {
      [使用AllowAnonymous]
      〔路线(RegisterExternal)]
      公共异步任务< IHttpActionResult> RegisterExternal(RegisterExternalBindingModel模型)
      {         如果(!ModelState.IsValid)
         {
            返回错误请求(ModelState中);
         }
         VAR accessTokenResponse = GenerateLocalAccessTokenResponse(model.UserName);
         返回OK(accessTokenResponse);
      }
    }

//生成访问令牌私有方法

 私人JObject GenerateLocalAccessTokenResponse(用户名字符串)
        {            变种tokenExpiration = TimeSpan.FromDays(1);
            ClaimsIdentity身份=新ClaimsIdentity(OAuthDefaults.AuthenticationType);
            identity.AddClaim(新索赔(ClaimTypes.Name,用户名));
            identity.AddClaim(新索赔(角色,用户));
            VAR道具=新AuthenticationProperties()
            {
                IssuedUtc = DateTime.UtcNow,
                ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration)
            };
            VAR票=新AuthenticationTicket(身份,道具);
            VAR的accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(票);
            JObject tokenResponse =新JObject(
                                        新JProperty(username的,用户名)
                                        新JProperty(的access_token的accessToken)
                                        //这是我需要的
                                        新JProperty(resfresh_token,GetRefreshToken()),
                                        新JProperty(token_type,承载),
                                        新JProperty(refresh_token,refreshToken)
                                        新JProperty(expires_in,tokenExpiration.TotalSeconds.ToString()),
                                        新JProperty(发行,ticket.Properties.IssuedUtc.ToString()),
                                        新JProperty(到期,ticket.Properties.ExpiresUtc.ToString())
        );
            返回tokenResponse;
        }


解决方案

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

1)更改ExternalLogin方法。
它通常是这样的:

 如果(hasRegistered)
{
     Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);     ClaimsIdentity oAuthIdentity =等待user.GenerateUserIdentityAsync(的UserManager,
                OAuthDefaults.AuthenticationType);
     ClaimsIdentity cookieIdentity =等待user.GenerateUserIdentityAsync(的UserManager,
                CookieAuthenticationDefaults.AuthenticationType);     AuthenticationProperties性能= ApplicationOAuthProvider.CreateProperties(user.UserName);
     Authentication.SignIn(属性,oAuthIdentity,cookieIdentity);
}

现在,实际上,有必要增加refresh_token。
方法是这样的:

 如果(hasRegistered)
{
     Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);     ClaimsIdentity oAuthIdentity =等待user.GenerateUserIdentityAsync(的UserManager,
                   OAuthDefaults.AuthenticationType);
     ClaimsIdentity cookieIdentity =等待user.GenerateUserIdentityAsync(的UserManager,
                    CookieAuthenticationDefaults.AuthenticationType);     AuthenticationProperties性能= ApplicationOAuthProvider.CreateProperties(user.UserName);     //加入此​​零件
     VAR票=新AuthenticationTicket(oAuthIdentity,属性);
     VAR的accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(票);                Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext语境=
                    新Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
                        Request.GetOwinContext(),
                        Startup.OAuthOptions.AccessTokenFormat,门票);     等待Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(背景);
     properties.Dictionary.Add(refresh_token,context.Token);     Authentication.SignIn(属性,oAuthIdentity,cookieIdentity);
}

现在的refrehs令牌将生成。

2)是使用基本context.SerializeTicket在SimpleRefreshTokenProvider CreateAsync方法的一个问题。
从消息<一个href=\"http://bitoftech.net/2014/08/11/asp-net-web-api-2-external-logins-social-logins-facebook-google-angularjs-app/\">Bit技术


  

似乎在ReceiveAsync方法中,context.DeserializeTicket是不
  在外部登录的情况下都返回一个身份验证票证。
  当我看着context.Ticket属性之后调用它为空。
  这相较于本地登录流程,DeserializeTicket方法
  设置context.Ticket属性的AuthenticationTicket。所以
  谜是DeserializeTicket怎么来表现不同
  两个流。在数据库中的受保护的票串被创建
  在同一CreateAsync方法,只有在不同的我称之为
  方法在GenerateLocalAccessTokenResponse,主场迎战Owin手动
  middlware称之为正常...而且,无论SerializeTicket或
  DeserializeTicket抛出一个错误...


所以,你需要使用Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer到searizize和反序列化售票。
这将是这样的:

  Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer串行
                =新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串=新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。事情是这样的:

 公众覆盖任务AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext上下文)
{
     VAR refreshToken = context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary[\"refresh_token\"];
     如果(!string.IsNullOrEmpty(refreshToken))
     {
          context.AdditionalResponseParameters.Add(refresh_token,refreshToken);
     }
     返回base.AuthorizationEndpointResponse(上下文);
}

所以..这就是一切!现在,在调用ExternalLogin方法后,你得到的网址:
<一href=\"https://localhost:44301/Account/ExternalLoginCallback?access_token=ACCESS_TOKEN&token_type=bearer&expires_in=300&state=STATE&refresh_token=TICKET&returnUrl=URL\">https://localhost:44301/Account/ExternalLoginCallback?access_token=ACCESS_TOKEN&token_type=bearer&expires_in=300&state=STATE&refresh_token=TICKET&returnUrl=URL

我希望这有助于)

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

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 ?

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

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

Now the refrehs token will be generated.

2) There is a problem to use basic context.SerializeTicket in SimpleRefreshTokenProvider CreateAsync method. Message from 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…

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

instead of:

token.ProtectedTicket = context.SerializeTicket();

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

instead of:

context.DeserializeTicket(refreshToken.ProtectedTicket);

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

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

I hope this helps)

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

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