如何与外部登录提供程序创建Refresh Token ?

时间:2021-11-17 19:45:47

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 :

我在网上找了半天也找不到解决问题的办法。我正在我的应用程序中实现OAuth。我正在使用。net Web API 2和Owin。场景是这样的,一旦用户请求到令牌端点,他或她将会收到一个访问令牌和一个刷新令牌,以生成一个新的访问令牌。我有一个类,帮助我生成一个refresh token。这里是:

   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 ?

现在我允许我的用户通过facebook注册。一旦用户注册了facebook,我就会生成一个访问令牌并把它给他。我还应该生成一个刷新令牌吗?我想到的一件事是,生成一个长访问令牌,比如有一天,然后这个用户必须再次登录facebook。但是如果我不想这样做,我可以给客户端一个refresh token,他可以用它来刷新生成的access token并获得一个新的。当有人注册或登录facebook或外部时,如何创建refresh token并将其附加到响应上?

Here is my external registration API

这是我的外部注册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;
        }

3 个解决方案

#1


23  

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:

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

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

现在,实际上,需要添加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);
}

Now the refrehs token will be generated.

现在将生成刷新hs标记。

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

使用基本上下文存在问题。SimpleRefreshTokenProvider CreateAsync方法中的SerializeTicket。来自Bit技术的消息

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…

在ReceiveAsync方法(上下文)中出现。DeserializeTicket并没有在所有外部登录的情况下返回身份验证票。当我看上下文的时候。在此之后,Ticket属性为null。与本地登录流相比,deserizeticket方法设置上下文。机票属性的认证。因此,现在的谜团是,在这两种流动中,反序列化的行为是如何不同的。数据库中受保护的票据字符串是用相同的CreateAsync方法创建的,不同之处在于,我在GenerateLocalAccessTokenResponse中手动调用该方法,而Owin middlware正常调用该方法…

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。将票证进行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));

instead of:

而不是:

token.ProtectedTicket = context.SerializeTicket();

And for ReceiveAsync method:

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

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:

现在您需要向ExternalLogin方法响应添加refresh_token。在您的oauthzationserverprovider中重写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);
}

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

所以. .这一切!现在,在调用ExternalLogin方法之后,您将获得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)

我希望这可以帮助)

#2


6  

@giraffe and others offcourse

@giraffe和其他人offcourse

A few remarks. There's no need to use the custom tickerserializer.

几句话。不需要使用自定义的tickerserializer。

The following line:

以下行:

Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context = 
                new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
                    Request.GetOwinContext(), 
                    Startup.OAuthOptions.AccessTokenFormat, ticket);

As tokenformat: Startup.OAuthOptions.AccessTokenFormat is used. Since we want to provide a refeshtoken this needs te be changed to: Startup.OAuthOptions.RefreshTokenFormat

tokenformat:Startup.OAuthOptions。AccessTokenFormat使用。因为我们想提供一个refeshtoken,所以需要将它更改为:Startup.OAuthOptions.RefreshTokenFormat。

Otherwise if you want to get a new accesstoken and refresh the refreshtoken ( grant_type=refresh_token&refresh_token=...... ) the deserializer/unprotector will fail. Since it uses the wrong purposes keywords at the decrypt stage.

否则,如果您想获得一个新的accesstoken并刷新refreshtoken (grant_type=refresh_token&refresh_token=…)反序列化器/ unprotector将失败。因为它在解密阶段使用了错误的关键字。

#3


5  

Finally found the solution for my problem. First of all, if you EVER encounter any problems with OWIN and you cannot figure out what is going wrong, I advise you to simply enable symbol-debugging and debug it. A great explanation can be found here: http://www.symbolsource.org/Public/Home/VisualStudio

终于找到了解决问题的办法。首先,如果您遇到OWIN方面的任何问题,并且不知道哪里出了问题,我建议您只需启用符号调试并调试它。这里有一个很好的解释:http://www.symbolsource.org/Public/Home/VisualStudio

My mistake simply was, that I was calculating a wrong ExiresUtc when using external login providers. So my refreshtoken basically was always expired right away....

我的错误是,在使用外部登录提供者时,我正在计算错误的ExiresUtc。所以我基本上refreshtoken总是马上过期....

If you are implementing refresh tokens, then look at this gread blog article: http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

如果您正在实现refresh token,那么请查看这篇gread博客文章:http://bitoftech.net4/07/16/enable - oauth-refreshtoken/201/201/2013/angularjs-app-using-asp-net -web-api-2-owin/

And to make it work with refresh tokens for external providers, you have to set the two requried parameters ("as:clientAllowedOrigin" and "as:clientRefreshTokenLifeTime") on the context so instead of

为了使它与外部提供程序的refresh token一起工作,您必须在上下文中设置两个请求参数(“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);

you need to get the client first and set the context parameters

您需要首先获得客户端并设置上下文参数


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

#1


23  

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:

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

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

现在,实际上,需要添加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);
}

Now the refrehs token will be generated.

现在将生成刷新hs标记。

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

使用基本上下文存在问题。SimpleRefreshTokenProvider CreateAsync方法中的SerializeTicket。来自Bit技术的消息

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…

在ReceiveAsync方法(上下文)中出现。DeserializeTicket并没有在所有外部登录的情况下返回身份验证票。当我看上下文的时候。在此之后,Ticket属性为null。与本地登录流相比,deserizeticket方法设置上下文。机票属性的认证。因此,现在的谜团是,在这两种流动中,反序列化的行为是如何不同的。数据库中受保护的票据字符串是用相同的CreateAsync方法创建的,不同之处在于,我在GenerateLocalAccessTokenResponse中手动调用该方法,而Owin middlware正常调用该方法…

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。将票证进行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));

instead of:

而不是:

token.ProtectedTicket = context.SerializeTicket();

And for ReceiveAsync method:

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

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:

现在您需要向ExternalLogin方法响应添加refresh_token。在您的oauthzationserverprovider中重写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);
}

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

所以. .这一切!现在,在调用ExternalLogin方法之后,您将获得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)

我希望这可以帮助)

#2


6  

@giraffe and others offcourse

@giraffe和其他人offcourse

A few remarks. There's no need to use the custom tickerserializer.

几句话。不需要使用自定义的tickerserializer。

The following line:

以下行:

Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context = 
                new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
                    Request.GetOwinContext(), 
                    Startup.OAuthOptions.AccessTokenFormat, ticket);

As tokenformat: Startup.OAuthOptions.AccessTokenFormat is used. Since we want to provide a refeshtoken this needs te be changed to: Startup.OAuthOptions.RefreshTokenFormat

tokenformat:Startup.OAuthOptions。AccessTokenFormat使用。因为我们想提供一个refeshtoken,所以需要将它更改为:Startup.OAuthOptions.RefreshTokenFormat。

Otherwise if you want to get a new accesstoken and refresh the refreshtoken ( grant_type=refresh_token&refresh_token=...... ) the deserializer/unprotector will fail. Since it uses the wrong purposes keywords at the decrypt stage.

否则,如果您想获得一个新的accesstoken并刷新refreshtoken (grant_type=refresh_token&refresh_token=…)反序列化器/ unprotector将失败。因为它在解密阶段使用了错误的关键字。

#3


5  

Finally found the solution for my problem. First of all, if you EVER encounter any problems with OWIN and you cannot figure out what is going wrong, I advise you to simply enable symbol-debugging and debug it. A great explanation can be found here: http://www.symbolsource.org/Public/Home/VisualStudio

终于找到了解决问题的办法。首先,如果您遇到OWIN方面的任何问题,并且不知道哪里出了问题,我建议您只需启用符号调试并调试它。这里有一个很好的解释:http://www.symbolsource.org/Public/Home/VisualStudio

My mistake simply was, that I was calculating a wrong ExiresUtc when using external login providers. So my refreshtoken basically was always expired right away....

我的错误是,在使用外部登录提供者时,我正在计算错误的ExiresUtc。所以我基本上refreshtoken总是马上过期....

If you are implementing refresh tokens, then look at this gread blog article: http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

如果您正在实现refresh token,那么请查看这篇gread博客文章:http://bitoftech.net4/07/16/enable - oauth-refreshtoken/201/201/2013/angularjs-app-using-asp-net -web-api-2-owin/

And to make it work with refresh tokens for external providers, you have to set the two requried parameters ("as:clientAllowedOrigin" and "as:clientRefreshTokenLifeTime") on the context so instead of

为了使它与外部提供程序的refresh token一起工作,您必须在上下文中设置两个请求参数(“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);

you need to get the client first and set the context parameters

您需要首先获得客户端并设置上下文参数


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