invalid_grant试图从谷歌获取oAuth令牌

时间:2012-05-14 00:41:12

标签: google-api

我在尝试从Google获取oAuth令牌以连接到他们的联系人API时遇到invalid_grant错误。所有的信息都是正确的,我已经检查了这一点,所以有点难过。

有谁知道可能导致此问题的原因?我已经尝试为它设置不同的客户端ID,但我得到相同的结果,我尝试连接许多不同的方式,包括尝试强制认证,但仍然是相同的结果。

28 个答案:

答案 0 :(得分:57)

尽管在我的请求中根据bonkydog的回答指定了“离线”access_type,但我遇到了同样的问题。长话短说我发现这里描述的解决方案对我有用:

https://groups.google.com/forum/#!topic/google-analytics-data-export-api/4uNaJtquxCs

实质上,当您在Google API控制台中添加OAuth2客户端时,Google会为您提供“客户端ID”和“电子邮件地址”(假设您选择“webapp”作为您的客户端类型)。尽管谷歌有误导性的命名惯例,但他们希望您在访问OAuth2 API时将“电子邮件地址”作为client_id参数的值发送。

调用这两个网址时都适用:

请注意,如果您使用“客户ID”而不是“电子邮件地址”来呼叫第一个网址,则成功。但是,尝试从第二个URL获取持有者令牌时,使用从该请求返回的代码将不起作用。相反,您将收到“错误400”和“invalid_grant”消息。

答案 1 :(得分:54)

当我将用户发送到OAuth时没有明确请求“离线”访问时,我遇到了这个问题“你想让这个应用程序获得触摸你的东西的权限吗?”页。

确保在请求中指定access_type = offline。

详细信息:https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(另外:我认为Google在2011年末添加了此限制。如果您之前有旧令牌,则需要将您的用户发送到权限页面以授权离线使用。)

答案 2 :(得分:44)

虽然这是一个老问题,但似乎很多人仍然遇到它 - 我们花了几天时间跟踪这个问题。

在OAuth2规范中," invalid_grant"对于与无效/过期/撤销令牌(auth grant或刷新令牌)相关的所有错误,它都是一种全能。

对我们来说,问题是双重的:

  1. 用户已主动撤销对我们应用的访问权限
    有道理,但得到这个:撤销后12小时, Google停止在他们的回复中发送错误消息: “error_description” : “Token has been revoked.”
    这是相当误导的,因为你会假设错误信息始终存在,而事实并非如此。您可以通过apps permission page检查您的应用是否仍然可以访问。

  2. 用户已重置/恢复其Google密码
    在2015年12月,Google changed他们的默认行为,以便非Google Apps用户重置密码会自动撤消所有用户的应用刷新令牌。在撤销时,错误消息遵循与之前情况相同的规则,因此您只能获得" error_description"在最初的12个小时内。似乎没有任何方式可以知道用户是否手动撤销访问(有意)或因密码重置(副作用)而发生。

  3. 除此之外,还有其他可能导致错误的其他潜在原因:

    1. 服务器时钟/时间不同步
    2. 未授权离线访问
    3. 受Google限制
    4. 使用过期的刷新令牌
    5. 用户已停用6个月
    6. 使用服务工作者电子邮件而非客户端ID
    7. 在短时间内访问令牌太多
    8. 客户端SDK可能已过时
    9. 不正确/不完整的刷新令牌
    10. I've written a short article总结了每个项目的一些调试指导,以帮助找到罪魁祸首。希望它有所帮助。

答案 3 :(得分:7)

我遇到了同样的问题。对我来说,我通过使用电子邮件地址(以... @ developer.gserviceaccount.com结尾的字符串)而不是client_id参数值的客户端ID来修复此问题。谷歌的命名设置令人困惑。

答案 4 :(得分:2)

我有同样的错误消息'invalid_grant',这是因为 的 authResult [ '代码'] 从客户端发送javascript未在服务器上正确收到。

尝试从服务器输出它以查看它是否正确而不是空字符串。

答案 5 :(得分:2)

我的问题是我使用了这个网址:

https://accounts.google.com/o/oauth2/token

当我应该使用此网址时:

https://www.googleapis.com/oauth2/v4/token

这是在测试一个希望离线访问Storage engine的服务帐户。

答案 6 :(得分:1)

就我而言,它是一个与原始请求不同的回调 URL。 因此,对于身份验证请求和代码交换,回调 URL 应该是相同的

答案 7 :(得分:1)

我们尝试了很多东西,最终问题是客户已经转向 在他们的 Google 帐户设置中关闭“不太安全的应用访问”。

要开启此功能:

  1. 转到https://myaccount.google.com/并管理帐户
  2. 转到“安全”标签
  3. 开启不太安全的应用访问

enter image description here

我希望这可以为某人节省一些时间!

答案 8 :(得分:1)

您在用户同意后在 URL 中获得的代码的有效期很短。请再次获取代码并尝试在几秒钟内获取访问令牌(您必须快点),它应该可以工作。我查不到代码的有效期,但确实很短。

答案 9 :(得分:1)

如果您正在邮递员/失眠症中对此进行测试,并且只是想使其正常运行,则提示:服务器身份验证代码(代码参数)只可以使用一次。这意味着,如果您在请求中填充其他任何参数并取回400,则需要使用新的服务器身份验证代码,否则将仅获得400。

答案 10 :(得分:1)

invalid_grant 错误有两个主要原因,您必须在刷新令牌和访问令牌的POST请求之前注意。

  1. 请求标头必须包含“content-type:application / x-www-form-urlencoded”
  2. 您的请求有效负载应该是url编码的Form Data,不要以json对象的形式发送。
  3. RFC 6749 OAuth 2.0 invalid_grant 定义为: 提供的授权授权(例如,授权代码,资源所有者凭据)或刷新令牌无效,已过期,已撤销,与授权请求中使用的重定向URI不匹配,或已发布给其他客户端。

    我找到了另一篇好文章,here你会发现很多其他原因导致这个错误。

    https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35

答案 11 :(得分:1)

您可能必须删除陈旧/无效的OAuth响应。

信用:node.js google oauth2 sample stopped working invalid_grant

注意:如果初始授权中使用的密码已更改,OAuth响应也将失效。

如果在bash环境中,您可以使用以下方法删除陈旧响应:

"editor.snippetSuggestions": "top",

答案 12 :(得分:1)

这是一个愚蠢的答案,但对我来说问题是我没有意识到我已经为我的谷歌用户发布了一个活跃的oAuth令牌,但我没有存储。在这种情况下,解决方案是转到api控制台并重置客户端密钥。

例如,关于此效果,还有许多其他答案 Reset Client Secret OAuth2 - Do clients need to re-grant access?

答案 13 :(得分:1)

使用Android clientId(没有client_secret)我收到以下错误响应:

{
 "error": "invalid_grant",
 "error_description": "Missing code verifier."
}

我找不到该字段的任何文档' code_verifier'但我发现如果你在授权和令牌请求中将它设置为相等的值,它将删除此错误。我不确定预期的价值应该是什么,或者它是否应该是安全的。它有一些最小长度(16个字符),但我发现设置为null也有效。

我在我的Android客户端中使用AppAuth作为授权请求,该客户端具有setCodeVerifier()功能。

AuthorizationRequest authRequest = new AuthorizationRequest.Builder(
                                    serviceConfiguration,
                                    provider.getClientId(),
                                    ResponseTypeValues.CODE,
                                    provider.getRedirectUri()
                            )
                            .setScope(provider.getScope())
                            .setCodeVerifier(null)
                            .build();

以下是节点中的示例令牌请求:

request.post(
  'https://www.googleapis.com/oauth2/v4/token',
  { form: {
    'code': '4/xxxxxxxxxxxxxxxxxxxx',
    'code_verifier': null,
    'client_id': 'xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
    'client_secret': null,
    'redirect_uri': 'com.domain.app:/oauth2redirect',
    'grant_type': 'authorization_code'
  } },
  function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log('Success!');
    } else {
      console.log(response.statusCode + ' ' + error);
    }

    console.log(body);
  }
);

我进行了测试,这适用于https://www.googleapis.com/oauth2/v4/tokenhttps://accounts.google.com/o/oauth2/token

如果您使用的是GoogleAuthorizationCodeTokenRequest

final GoogleAuthorizationCodeTokenRequest req = new GoogleAuthorizationCodeTokenRequest(
                    TRANSPORT,
                    JSON_FACTORY,
                    getClientId(),
                    getClientSecret(),
                    code,
                    redirectUrl
);
req.set("code_verifier", null);          
GoogleTokenResponse response = req.execute();

答案 14 :(得分:1)

如果您使用的是scribe库,只需设置离线模式,就像bonkydog建议的那样 这是代码:

OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
                .callback(callbackUrl).scope(SCOPE).offline(true)
                .build();

https://github.com/codolutions/scribe-java/

答案 15 :(得分:0)

对于未来的人们...我读了许多文章和博客,但是在下面的解决方案方面很幸运...

GoogleTokenResponse tokenResponse =
      new GoogleAuthorizationCodeTokenRequest(
          new NetHttpTransport(),
          JacksonFactory.getDefaultInstance(),
          "https://www.googleapis.com/oauth2/v4/token",
          clientId,
          clientSecret,
          authCode,
          "") //Redirect Url
     .setScopes(scopes)
     .setGrantType("authorization_code")
     .execute();

This博客描述了出现“ invalid_grant”错误的不同情况。

享受!!!

答案 16 :(得分:0)

对我来说,我必须确保redirect_uri与开发者控制台Authorised redirect URIs中的那个完全匹配,为我修复了它,我能够调试并知道确切的含义从切换后的问题 https://accounts.google.com/o/oauth2/tokenhttps://www.googleapis.com/oauth2/v4/token

我遇到了错误:

{"error": "redirect_uri_mismatch",  "error_description": "Bad Request"}

答案 17 :(得分:0)

在Google控制台上启用新的服务API并尝试使用以前制作的凭据后,我遇到了这个问题。

要解决此问题,我必须返回到凭证页面,单击凭证名称,然后再次单击“保存” 。之后,我可以进行身份​​验证。

答案 18 :(得分:0)

就我而言,问题出在我的代码中。错误地,我尝试使用相同的令牌启动客户端2次。如果以上答案均不能帮助确保您没有生成该客户端的2个实例。

修复之前的代码:

def gc_service
      oauth_client = Signet::OAuth2::Client.new(client_options)
      oauth_client.code = params[:code]
      response = oauth_client.fetch_access_token!
      session[:authorization] = response
      oauth_client.update!(session[:authorization])

      gc_service = Google::Apis::CalendarV3::CalendarService.new
      gc_service.authorization = oauth_client

      gc_service
    end
primary_calendar_id = gc_service.list_calendar_lists.items.select(&:primary).first.id

gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

将其更改为(仅使用一个实例):

@gc_service = gc_service
primary_calendar_id = @gc_service.list_calendar_lists.items.select(&:primary).first.id

@gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

它解决了我的赠款类型问题。

答案 19 :(得分:0)

对我来说,问题是我的项目中有多个客户,我可以肯定这是完全可以的,但是我删除了该项目的所有客户,并创建了一个新客户,所有这些都开始为我工作( WP_SMTP插件帮助支持论坛)我无法找到该链接以供参考

答案 20 :(得分:0)

如果要清理用户输入(例如php中的$_GET["code"]),请确保不要意外替换代码中的某些内容。

我正在使用的正则表达式现在为/[^A-Za-z0-9\/-]/

答案 21 :(得分:0)

看看这个documentation

首先,您需要一个access_token:

$code = $_GET['code'];

$clientid = "xxxxxxx.apps.googleusercontent.com";
$clientsecret = "xxxxxxxxxxxxxxxxxxxxx";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&code=".urlencode($code)."&grant_type=authorization_code&redirect_uri=". urlencode("https://yourdomain.com"));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$refresh_token = $server_output->refresh_token;
$expires_in = $server_output->expires_in;

在数据库中保护访问令牌和刷新令牌以及expire_in。访问令牌在$ expires_in秒后过期。不需要通过以下请求获取新的访问令牌(并将其保存在数据库中):

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&refresh_token=".urlencode($refresh_token)."&grant_type=refresh_token");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$expires_in = $server_output->expires_in;

请记住,将redirect_uri域添加到您的Google控制台中的域中:https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988在“ OAuth 2.0-Client-IDs”标签中。您还可以在其中找到您的客户ID和客户秘密。

答案 22 :(得分:0)

从第一次将用户重定向到google身份验证页面(并获取代码)到获取返回的代码并将其发布到令牌网址之间,这段时间没有记录。与实际的google提供的client_id(而不是“未公开的电子邮件地址”)相对,它对我来说效果很好。我只需要再次开始该过程。

答案 23 :(得分:0)

尝试将您的网址更改为

 android:layout_centerHorizontal="true"
 android:layout_centerVertical="true"
 android:layout_centerInParent="true"

答案 24 :(得分:0)

通过在项目的Google控制台中删除所有授权的重定向URI来解决。 当您将“ postmessage”用作重定向URI时,我会使用服务器端流程

答案 25 :(得分:0)

在考虑并尝试了所有其他方法之后,我在这里解决了如何使用googleapis模块结合request模块解决nodejs中的问题,我曾用这个模块获取标记而不是提供的getToken()方法:

const request = require('request');

//SETUP GOOGLE AUTH
var google = require('googleapis');
const oAuthConfigs = rootRequire('config/oAuthConfig')
const googleOAuthConfigs = oAuthConfigs.google

//for google OAuth: https://github.com/google/google-api-nodejs-client
var OAuth2 = google.auth.OAuth2;
var googleOAuth2Client = new OAuth2(
    process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId, 
    process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret, 
    process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl);

/* generate a url that asks permissions for Google+ and Google Calendar scopes
https://developers.google.com/identity/protocols/googlescopes#monitoringv3*/
var googleOAuth2ClientScopes = [
    'https://www.googleapis.com/auth/plus.me',
    'https://www.googleapis.com/auth/userinfo.email'
];

var googleOAuth2ClientRedirectURL = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl; 

var googleOAuth2ClientAuthUrl = googleOAuth2Client.generateAuthUrl({
  access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
  scope: googleOAuth2ClientScopes // If you only need one scope you can pass it as string
});

//AFTER SETUP, THE FOLLOWING IS FOR OBTAINING TOKENS FROM THE AUTHCODE


        const ci = process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId
        const cs = process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret
        const ru = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl
        var oauth2Client = new OAuth2(ci, cs, ru);

        var hostUrl = "https://www.googleapis.com";
        hostUrl += '/oauth2/v4/token?code=' + authCode + '&client_id=' + ci + '&client_secret=' + cs + '&redirect_uri=' + ru + '&grant_type=authorization_code',
        request.post({url: hostUrl}, function optionalCallback(err, httpResponse, data) {
            // Now tokens contains an access_token and an optional refresh_token. Save them.
            if(!err) {
                //SUCCESS! We got the tokens
                const tokens = JSON.parse(data)
                oauth2Client.setCredentials(tokens);

                //AUTHENTICATED PROCEED AS DESIRED.
                googlePlus.people.get({ userId: 'me', auth: oauth2Client }, function(err, response) {
                // handle err and response
                    if(!err) {
                        res.status(200).json(response);
                    } else {
                        console.error("/google/exchange 1", err.message);
                        handleError(res, err.message, "Failed to retrieve google person");
                    }
                });
            } else {
                console.log("/google/exchange 2", err.message);
                handleError(res, err.message, "Failed to get access tokens", err.code);
            }
        });

我只需使用request通过HTTP发出api请求,如下所述: here

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code

答案 26 :(得分:0)

在这个网站上 console.developers.google.com

此控制台选择您的项目输入誓言网址。 当oauth成功时,oauth回调网址将重定向

答案 27 :(得分:0)

对我来说,这是由使用相同代码的后续 getToken 调用引起的。

也就是说,在 NestJS 中,我的回调端点用 @UseGuards(AuthGuard('google')) 装饰,我尝试在回调中调用 getToken