如何使用刷新令牌更新访问令牌?

Posted

技术标签:

【中文标题】如何使用刷新令牌更新访问令牌?【英文标题】:How to renew the access token using the refresh token? 【发布时间】:2014-09-13 17:03:52 【问题描述】:

我正在使用 ASP.NET MVC 5OWIN

我做了很多研究,还没有找到如何使用刷新令牌来更新访问令牌。

我的场景是:用户第一次访问我的应用程序时,他或她授予对我读取从 API 返回的刷新令牌的帐户的访问权限。当用户回到我的应用程序时,我需要根据“刷新令牌”刷新访问令牌。

谁能提供一些代码?

这是我到目前为止所取得的成就:

Startup.Auth.cs:

    var googleOAuth2AuthenticationOptions = new GoogleOAuth2AuthenticationOptions
    
        Caption = "Google+",
        ClientId = Parameters.Instance.Authentication.oAuth.GooglePlus.ClientId,
        ClientSecret = Parameters.Instance.Authentication.oAuth.GooglePlus.ClientSecret,
        CallbackPath = new PathString("/oauth-login-return"),
        Provider = new GoogleOAuth2AuthenticationProvider
        
            OnAuthenticated = async context =>
            
                context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
                context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Identity.FindFirstValue(ClaimTypes.Email)));
                context.Identity.AddClaim(new Claim("picture", context.User.GetValue("picture").ToString()));
                context.Identity.AddClaim(new Claim("profile", context.User.GetValue("profile").ToString()));
                context.Identity.AddClaim(
                    new Claim(Parameters.Instance.Authentication.oAuth.GooglePlus.AccessTokenClaimType,
                        context.AccessToken));
            
        
    ;
    googleOAuth2AuthenticationOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
    googleOAuth2AuthenticationOptions.Scope.Add("https://www.googleapis.com/auth/userinfo.email");

身份验证控制器:

[HttpPost]
[AllowAnonymous]
public ActionResult ExternalLogin(string provider, string returnUrl)

    RedirectIfAuthenticated();

    return new ChallengeResult(provider, Url.Content("~/oauth-login-callback"));


[ActionName("oauth-login-back")]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)



// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";

private IAuthenticationManager AuthenticationManager

    get
    
        return HttpContext.GetOwinContext().Authentication;
    


private class ChallengeResult : HttpUnauthorizedResult

    public ChallengeResult(string provider, string redirectUri)
        : this(provider, redirectUri, null)
    
    

    private ChallengeResult(string provider, string redirectUri, string userId)
    
        LoginProvider = provider;
        RedirectUri = redirectUri;
        UserId = userId;
    

    private string LoginProvider  get; set; 

    private string RedirectUri  get; set; 

    private string UserId  get; set; 

    public override void ExecuteResult(ControllerContext context)
    
        var properties = new AuthenticationProperties  RedirectUri = RedirectUri ;
        if (UserId != null)
        
            properties.Dictionary[XsrfKey] = UserId;
        
        context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
    

【问题讨论】:

您是如何获得 OAuth Bearer 令牌的? @ErikPhilips 我插入了一些代码摘录。请检查一下。 OWIN Security - How to Implement OAuth2 Refresh Tokens 的可能重复项 @erikphilips 抱歉,erik,但我在阅读该链接后没有明白这一点。我是否应该构建一个 oauth 身份验证提供程序,但我正在使用 OWIN GOOGLE?谢谢。 @ErikPhilips 请检查我的答案。 【参考方案1】:

这个问题一点也不重复。我希望这可以帮助其他人没有像我一样度过的日子。

将近 4 天后,我发现了如何使用 OWIN 在 google api 中获取新的访问令牌。

我将发布解决方案,但首先,我必须说,帮助我开始了解我的错误的线索是为 Katana 项目设置调试符号。请参阅此链接: http://www.symbolsource.org/Public/Home/VisualStudio

此图显示了如何配置调试符号服务器。

这个显示了正在加载的 Katana 调试符号。

之后,我发现我的问题是 Google API 返回 403: Forbidden

“未配置访问。请使用 Google Developers Console 为您的项目激活 API”

然后,在堆栈溢出时发现这个帖子: "Access Not Configured. Please use Google Developers Console to activate the API for your project."

更具体地说这个答案:https://***.com/a/24401189/833846

之后,我去了 Google Developers Console 并设置了 Google+ API

然后,瞧!它奏效了。

现在,使用刷新令牌获取新访问令牌的代码(我还没有找到任何使用 OWIN API 完成该操作的方法)。

public static class TokenValidator

    /// <summary>
    /// Obtém um novo access token na API do google.
    /// </summary>
    /// <param name="clientId"></param>
    /// <param name="clientSecret"></param>
    /// <param name="refreshToken"></param>
    /// <returns></returns>
    public static GoogleRefreshTokenModel ValidateGoogleToken(string clientId, string clientSecret, string refreshToken)
    
        const string url = "https://accounts.google.com/o/oauth2/token";

        var parameters = new List<KeyValuePair<string, string>>
        
            new KeyValuePair<string, string>("client_id", clientId),
            new KeyValuePair<string, string>("client_secret", clientSecret),
            new KeyValuePair<string, string>("grant_type", "refresh_token"),
            new KeyValuePair<string, string>("refresh_token", refreshToken)
        ;

        var content = GetContentAsync(url, "POST",  parameters);

        var token = JsonConvert.DeserializeObject<GoogleRefreshTokenModel>(content);

        return token;
    

    private static string GetContentAsync(string url, 
        string method = "POST",
        IEnumerable<KeyValuePair<string, string>> parameters = null)
    
        return method == "POST" ? PostAsync(url, parameters) : GetAsync(url, parameters);
    

    private static string PostAsync(string url, IEnumerable<KeyValuePair<string, string>> parameters = null)
    
        var uri = new Uri(url);

        var request = WebRequest.Create(uri) as HttpWebRequest;
        request.Method = "POST";
        request.KeepAlive = true;
        request.ContentType = "application/x-www-form-urlencoded";

        var postParameters = GetPostParameters(parameters);

        var bs = Encoding.UTF8.GetBytes(postParameters);
        using (var reqStream = request.GetRequestStream())
        
            reqStream.Write(bs, 0, bs.Length);
        

        using (var response = request.GetResponse())
        
            var sr = new StreamReader(response.GetResponseStream());
            var jsonResponse = sr.ReadToEnd();
            sr.Close();

            return jsonResponse;
        
    

    private static string GetPostParameters(IEnumerable<KeyValuePair<string, string>> parameters = null)
    
        var postParameters = string.Empty;
        foreach (var parameter in parameters)
        
            postParameters += string.Format("&0=1", parameter.Key,
                HttpUtility.htmlEncode(parameter.Value));
        
        postParameters = postParameters.Substring(1);

        return postParameters;
    

    private static string GetAsync(string url, IEnumerable<KeyValuePair<string, string>> parameters = null)
    
        url += "?" + GetQueryStringParameters(parameters);

        var forIdsWebRequest = WebRequest.Create(url);
        using (var response = (HttpWebResponse)forIdsWebRequest.GetResponse())
        
            using (var data = response.GetResponseStream())
            using (var reader = new StreamReader(data))
            
                var jsonResponse = reader.ReadToEnd();

                return jsonResponse;
            
        
    

    private static string GetQueryStringParameters(IEnumerable<KeyValuePair<string, string>> parameters = null)
    
        var queryStringParameters = string.Empty;
        foreach (var parameter in parameters)
        
            queryStringParameters += string.Format("&0=1", parameter.Key,
                HttpUtility.HtmlEncode(parameter.Value));
        
        queryStringParameters = queryStringParameters.Substring(1);

        return queryStringParameters;
    

重要 1: 要获得刷新令牌,您必须在“ExecuteResult”方法中将“access_type”设置为“offline”,这样:

properties.Dictionary["access_type"] = "offline";

重要提示 2: 获得刷新令牌后,您必须将其存储在某个安全源中。 Google API 不会向您发出新的刷新令牌,除非您在调用该行之前将“approval_prompt”设置为“强制”(以相同的方法):

context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);

我还建议看看:

Google API Offline Access

Google OAUTH 2.0 Playground

Google API Discovery Check

【讨论】:

【参考方案2】:

过去两天自己都在研究如何更新访问令牌。答案在这里的另一个线程中发布:

How Google API V 3.0 .Net library and Google OAuth2 Handling refresh token

【讨论】:

以上是关于如何使用刷新令牌更新访问令牌?的主要内容,如果未能解决你的问题,请参考以下文章

我们应该与访问令牌一起更新刷新令牌吗?

如何使用刷新令牌刷新访问令牌?

Cognito 用户池:如何使用刷新令牌刷新访问令牌

使用刷新令牌和访问令牌如何比仅使用 1 个 JWT 更“安全”?

如何在资源请求上重新生成刷新令牌和访问令牌?

访问令牌和刷新令牌最佳实践?如何实现访问和刷新令牌