首页 > 解决方案 > 如何实现 Google API 刷新令牌

问题描述

我一直在从事一个项目,我必须在 7 个不同的平台上实施 OAuth,但由于某种原因,Google 的 OAuth 流程让我感到困惑。

该项目是在 .NET MVC 中开发的,我正在使用 RestSharp 发出传出 Http 请求。

我正在检索 OAuth 请求以访问 GoogleAnalytics API,并且在第一次尝试时,我获得了一个有效的访问令牌,我可以使用该令牌成功地从用户的 GA 帐户中检索数据。但是,尽管我在身份验证请求 URL 中指定了 access_type=offline,但在令牌过期后尝试刷新令牌时,我看到以下错误:

400 - 错误请求 - {“错误”:“invalid_grant”,“错误描述”:“错误请求”}

我的请求 URL 如下所示:

https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https://www.googleapis.com/auth/analytics.readonly&include_granted_scopes=true&response_type=code&state=8f692f0f-b177-4b0b-aa89- a757da9432e3&redirect_uri=https://localhost:44338/GoogleAnalytics&client_id=xxxx

当用户与代码一起被重定向回来时,我的令牌请求函数被调用:

public OAuth2Token GetToken(string code)
{
    var client = new RestClient(_tokenUri);
    var request = new RestRequest(Method.POST);
    request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
    request.AddParameter("undefined", $"client_id={_clientId}&client_secret={_secret}&code={code}&redirect_uri={_redirectUri}&grant_type=authorization_code", ParameterType.RequestBody);
    var response = client.Execute<OAuth2Token>(request);
    return response.Data;
}

访问和刷新令牌被返回,并存储在数据库中,然后在我的刷新函数中使用,这里的 tokenUri 是https://www.googleapis.com/oauth2/v4/token

public OAuth2Token RefreshToken()
{
    var client = new RestClient(_tokenUri);
    var request = new RestRequest(Method.POST);
    request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
    request.AddParameter("undefined", $"client_id={_clientId}&client_secret={_secret}&refresh_token={_refreshToken}&grant_type=refresh_token", ParameterType.RequestBody);
    var response = client.Execute<OAuth2Token>(request);
    if (!response.IsSuccessful)
        if (response.ErrorException == null)
            throw new Exception(response.Content); 
        else
            throw response.ErrorException;
    return response.Data;
}

还有一点需要注意的是,我这里的 Google App 处于测试模式,并且我已经将用户添加到了测试用户列表中。关于我在这里做错了什么有什么建议吗?

干杯

标签: c#oauth-2.0oauthgoogle-apigoogle-oauth

解决方案


帖子请求希望看到帖子正文中的值。您将它们作为参数发送。

  request.AddParameter("undefined", $"client_id={_clientId}&client_secret={_secret}&refresh_token={_refreshToken}&grant_type=refresh_token", ParameterType.RequestBody);

您可能应该使用 request.AddBody

 request.AddBody("undefined", $"client_id={_clientId}&client_secret={_secret}&refresh_token={_refreshToken}&grant_type=refresh_token", ParameterType.RequestBody);

详细解释。

让我们从顶部开始。

谷歌授权或实际上所有的 Oauth2 服务器都会执行所谓的 Oauth 舞蹈,这个舞蹈有三个步骤。

请求同意

您需要的第一个 URL 将导致授权服务器向用户显示同意屏幕。

GET https://accounts.google.com/o/oauth2/auth?client_id={clientid}&redirect_uri={redirectURI}&scope={scope}&response_type=code

这将返回给您一个授权代码注意响应类型是 response_type=code

交换访问令牌和刷新令牌的授权代码。

这个调用是一个 HTTP Post 并且正文是一个 HTTP 请求字符串的形式。

POST https://accounts.google.com/o/oauth2/token
code={AUTHORIZATION CODE}&client_id={ClientId}&client_secret={ClientSecret}&redirect_uri={REDIRECT URI}&grant_type=authorization_code

这里的响应将是 Json 格式,将是一个刷新令牌和一个访问令牌。注意这里的grant_type 是授权码。

{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}

由于您的访问令牌将在一小时内到期,您需要使用刷新令牌对其进行刷新

刷新访问令牌。

这也是一个 HTTP POST 调用,并且正文再次采用查询字符串的形式。注意这里的授权类型是刷新令牌

POST https://accounts.google.com/o/oauth2/token
client_id={CLIENT ID}&client_secret={ClientSecret}&refresh_token={REFRESH TOKEN}&grant_type=refresh_token

然后你有一个新的访问令牌。

{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}

推荐阅读