invalid_grant试图从谷歌中获取oAuth令牌

时间:2022-01-31 15:46:30

I keep getting an invalid_grant error on trying to get an oAuth token from Google to connect to their contacts api. All the information is correct and I have tripple checked this so kind of stumped.

在试图从谷歌获取oAuth令牌以连接到它们的contacts api时,我不断地得到一个invalid_grant错误。所有的信息都是正确的,我已经查过了。

Does anyone know what may be causing this issue? I have tried setting up a different client id for it but I get the same result, I have tried connecting many different ways including trying the force authentication, but still the same result.

有人知道是什么引起了这个问题吗?我尝试过为它设置一个不同的客户机id,但是我得到了相同的结果,我尝试过连接许多不同的方法,包括尝试强制验证,但结果仍然是一样的。

14 个解决方案

#1


45  

I ran into this problem when I didn't explicitly request "offline" access when sending the user to the OAuth "Do you want to give this app permission to touch your stuff?" page.

当我将用户发送到OAuth“你想让这个应用允许触摸你的东西吗?”页面时,我没有显式地请求“脱机”访问时遇到了这个问题。

Make sure you specify access_type=offline in your request.

确保在请求中指定access_type=脱机。

Details here: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

详细信息:https://developers.google.com/accounts/docs/OAuth2WebServer离线

(Also: I think Google added this restriction in late 2011. If you have old tokens from before then, you'll need to send your users to the permission page to authorize offline use.)

(另外:我认为谷歌在2011年末增加了这个限制。如果您有以前的令牌,您需要将您的用户发送到权限页来授权脱机使用)。

#2


53  

I ran into this same problem despite specifying the "offline" access_type in my request as per bonkydog's answer. Long story short I found that the solution described here worked for me:

尽管按照bonkydog的回答在请求中指定了“脱机”access_type,但我还是遇到了同样的问题。长话短说,我发现这里描述的解决方案对我起了作用:

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

https://groups.google.com/forum/ # ! / google-analytics-data-export-api / 4 unajtquxcs话题

In essence, when you add an OAuth2 Client in your Google API's console Google will give you a "Client ID" and an "Email address" (assuming you select "webapp" as your client type). And despite Google's misleading naming conventions, they expect you to send the "Email address" as the value of the client_id parameter when you access their OAuth2 API's.

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

This applies when calling both of these URL's:

这适用于调用这两个URL时:

Note that the call to the first URL will succeed if you call it with your "Client ID" instead of your "Email address". However using the code returned from that request will not work when attempting to get a bearer token from the second URL. Instead you will get an 'Error 400' and an "invalid_grant" message.

注意,如果您使用“客户端ID”而不是“电子邮件地址”来调用第一个URL,那么调用将成功。但是,当试图从第二个URL获取无记名令牌时,使用从该请求返回的代码将不起作用。相反,您将得到一个“Error 400”和一个“invalid_grant”消息。

#3


34  

Although this is an old question, it seems like many still encounter it - we spent days on end tracking this down ourselves.

虽然这是一个老问题,但似乎很多人仍然会遇到它——我们花了好几天的时间来追踪这个问题。

In the OAuth2 spec, "invalid_grant" is sort of a catch-all for all errors related to invalid/expired/revoked tokens (auth grant or refresh token).

在OAuth2规范中,“invalid_grant”是所有与无效/过期/取消令牌(auth grant或refresh token)相关的错误的统称。

For us, the problem was two-fold:

对我们来说,问题有两方面:

  1. User has actively revoked access to our app
    Makes sense, but get this: 12 hours after revocation, Google stops sending the error message in their response: “error_description” : “Token has been revoked.”
    It's rather misleading because you'll assume that the error message is there at all times which is not the case. You can check whether your app still has access at the apps permission page.

    用户已经主动撤销对我们应用程序的访问,这是有意义的,但是可以得到:撤销12小时后,谷歌停止发送错误消息,在他们的响应中:“error_description”:“Token已被撤销。”这是相当具有误导性的,因为您将假定错误消息一直存在,但事实并非如此。你可以检查你的应用程序在应用程序许可页面上是否还有访问权限。

  2. User has reset/recovered their Google password
    In December 2015, Google changed their default behaviour so that password resets for non-Google Apps users would automatically revoke all the user's apps refresh tokens. On revocation, the error message follows the same rule as the case before, so you'll only get the "error_description" in the first 12 hours. There doesn't seem to be any way of knowing whether the user manually revoked access (intentful) or it happened because of a password reset (side-effect).

    用户在2015年12月重置了谷歌密码,谷歌改变了他们的默认行为,让非Google Apps用户的密码重置将自动撤销所有用户的应用程序刷新令牌。在撤销时,错误消息遵循与前面的情况相同的规则,因此您只会在前12小时内获得“error_description”。似乎没有办法知道用户是否手动撤销了访问(intentful),还是因为密码重置(副作用)而导致的。

Apart from those, there's a myriad of other potential causes that could trigger the error:

除了这些,还有无数其他潜在的原因可能引发错误:

  1. Server clock/time is out of sync
  2. 服务器时钟/时间不同步
  3. Not authorized for offline access
  4. 未授权离线访问。
  5. Throttled by Google
  6. 扼杀了谷歌
  7. Using expired refresh tokens
  8. 使用刷新令牌到期
  9. User has been inactive for 6 months
  10. 用户已停用6个月
  11. Use service worker email instead of client ID
  12. 使用服务人员电子邮件而不是客户ID。
  13. Too many access tokens in short time
  14. 在短时间内太多的访问令牌。
  15. Client SDK might be outdated
  16. 客户端SDK可能已经过时了。
  17. Incorrect/incomplete refresh token
  18. 不正确的/不完整的刷新令牌

I've written a short article summarizing each item with some debugging guidance to help find the culprit. Hope it helps.

我已经写了一篇简短的文章,总结了每个条目,并提供了一些调试指导,以帮助查找罪魁祸首。希望它可以帮助。

#4


7  

I encountered the same problem. For me, I fixed this by using Email Address (the string that ends with ...@developer.gserviceaccount.com) instead of Client ID for client_id parameter value. The naming set by Google is confusing here.

我遇到了同样的问题。对于我来说,我通过使用电子邮件地址(以…@developer.gserviceaccount.com结尾的字符串)来解决这个问题,而不是使用client_id参数值的客户机ID。谷歌的命名是令人费解的。

#5


2  

I had the same error message 'invalid_grant' and it was because the authResult['code'] send from client side javascript was not received correctly on the server.

我有相同的错误消息“invalid_grant”,这是因为服务器上没有正确地接收到来自客户端javascript的authResult['code']。

Try to output it back from the server to see if it is correct and not an empty string.

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

#6


2  

My issue was that I used this URL:

我的问题是我使用了这个URL:

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

When I should have used this URL:

当我应该使用这个URL:

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

This was testing a service account which wanted offline access to the Storage engine.

这是在测试一个希望离线访问存储引擎的服务帐户。

#7


1  

if you are using scribe library, just set up the offline mode, like bonkydog suggested here is the code:

如果您正在使用scriptlibrary,只需设置离线模式,就像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/

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

#8


1  

in this site console.developers.google.com

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

this console board select your project input the oath url. the oauth callback url will redirect when the oauth success

控制台选择您的项目输入誓言url。oauth回调url将在oauth成功时重定向

#9


1  

Using a Android clientId (no client_secret) I was getting the following error response:

使用Android clientId(无client_secret),我得到了以下错误响应:

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

I cannot find any documentation for the field 'code_verifier' but I discovered if you set it to equal values in both the authorization and token requests it will remove this error. I'm not sure what the intended value should be or if it should be secure. It has some minimum length (16? characters) but I found setting to null also works.

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

I am using AppAuth for the authorization request in my Android client which has a setCodeVerifier() function.

我在Android客户机中使用AppAuth进行授权请求,它具有setCodeVerifier()函数。

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

Here is an example token request in node:

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

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

I tested and this works with both https://www.googleapis.com/oauth2/v4/token and https://accounts.google.com/o/oauth2/token.

我测试了它,它可以使用https://www.googleapis.com/oauth2/v4/token和https://accounts.google.com/o/oauth2/token。

If you are using GoogleAuthorizationCodeTokenRequest instead:

如果您使用的是GoogleAuthorizationCodeTokenRequest而不是:

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

#10


1  

This is a silly answer, but the problem for me was that I failed to realize I already had been issued an active oAuth token for my google user which I failed to store. The solution in this case is to go to the api console and reset the client secret.

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

There are numerous other answers on SO to this effect for example Reset Client Secret OAuth2 - Do clients need to re-grant access?

关于这个问题还有很多其他的答案,例如重置客户端秘密OAuth2——客户端是否需要重新授予访问权限?

#11


1  

You might have to remove a stale/invalid OAuth response.

您可能需要删除陈旧/无效的OAuth响应。

Credit: node.js google oauth2 sample stopped working invalid_grant

信贷:节点。js谷歌oauth2样本停止工作,invalid_grant

Note: An OAuth response will also become invalid if the password used in the initial authorization has been changed.

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

If in a bash environment, you can use the following to remove the stale response:

如果在bash环境中,您可以使用以下内容来删除陈旧的响应:

rm /Users/<username>/.credentials/<authorization.json>

rm /用户/ <用户名> / .credentials / < authorization.json >

#12


1  

There are two major reasons for invalid_grant error which you have to take care prior to the POST request for Refresh Token and Access Token.

存在invalid_grant错误的两个主要原因,您必须在对Refresh Token和Access Token的POST请求之前注意这些原因。

  1. Request header must contain "content-type: application/x-www-form-urlencoded"
  2. 请求头必须包含“内容类型:应用程序/x-www-form- urlencodes”
  3. Your request payload should be url encoded Form Data, don't send as json object.
  4. 请求有效负载应该是url编码的表单数据,不要以json对象发送。

RFC 6749 OAuth 2.0 defined invalid_grant as: The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.

RFC 6749 OAuth 2.0将invalid_grant定义为:所提供的授权授予(例如,授权码、资源所有者凭证)或refresh token无效、过期、撤销、不匹配授权请求中使用的重定向URI,或被分发给另一个客户端。

I found another good article, here you will find many other reasons for this error.

我发现了另一篇好文章,在这里你会发现许多其他的原因导致这个错误。

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

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

#13


0  

After considering and trying all of the other ways here, here's how I solved the issue in nodejs with the googleapis module in conjunction with the request module, which I used to fetch the tokens instead of the provided getToken() method:

在考虑并尝试了这里的所有其他方法之后,下面是我如何使用nodejs和googleapis模块与request模块一起解决这个问题的方法。

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

I simply use request to make the api request via HTTP as described here: https://developers.google.com/identity/protocols/OAuth2WebServer#offline

我只是使用请求通过这里描述的HTTP发出api请求:https://developers.google.com/identity/protocols/OAuth2WebServer#脱机

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

#14


0  

Try change your url for requst to

尝试更改requst的url

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

#1


45  

I ran into this problem when I didn't explicitly request "offline" access when sending the user to the OAuth "Do you want to give this app permission to touch your stuff?" page.

当我将用户发送到OAuth“你想让这个应用允许触摸你的东西吗?”页面时,我没有显式地请求“脱机”访问时遇到了这个问题。

Make sure you specify access_type=offline in your request.

确保在请求中指定access_type=脱机。

Details here: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

详细信息:https://developers.google.com/accounts/docs/OAuth2WebServer离线

(Also: I think Google added this restriction in late 2011. If you have old tokens from before then, you'll need to send your users to the permission page to authorize offline use.)

(另外:我认为谷歌在2011年末增加了这个限制。如果您有以前的令牌,您需要将您的用户发送到权限页来授权脱机使用)。

#2


53  

I ran into this same problem despite specifying the "offline" access_type in my request as per bonkydog's answer. Long story short I found that the solution described here worked for me:

尽管按照bonkydog的回答在请求中指定了“脱机”access_type,但我还是遇到了同样的问题。长话短说,我发现这里描述的解决方案对我起了作用:

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

https://groups.google.com/forum/ # ! / google-analytics-data-export-api / 4 unajtquxcs话题

In essence, when you add an OAuth2 Client in your Google API's console Google will give you a "Client ID" and an "Email address" (assuming you select "webapp" as your client type). And despite Google's misleading naming conventions, they expect you to send the "Email address" as the value of the client_id parameter when you access their OAuth2 API's.

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

This applies when calling both of these URL's:

这适用于调用这两个URL时:

Note that the call to the first URL will succeed if you call it with your "Client ID" instead of your "Email address". However using the code returned from that request will not work when attempting to get a bearer token from the second URL. Instead you will get an 'Error 400' and an "invalid_grant" message.

注意,如果您使用“客户端ID”而不是“电子邮件地址”来调用第一个URL,那么调用将成功。但是,当试图从第二个URL获取无记名令牌时,使用从该请求返回的代码将不起作用。相反,您将得到一个“Error 400”和一个“invalid_grant”消息。

#3


34  

Although this is an old question, it seems like many still encounter it - we spent days on end tracking this down ourselves.

虽然这是一个老问题,但似乎很多人仍然会遇到它——我们花了好几天的时间来追踪这个问题。

In the OAuth2 spec, "invalid_grant" is sort of a catch-all for all errors related to invalid/expired/revoked tokens (auth grant or refresh token).

在OAuth2规范中,“invalid_grant”是所有与无效/过期/取消令牌(auth grant或refresh token)相关的错误的统称。

For us, the problem was two-fold:

对我们来说,问题有两方面:

  1. User has actively revoked access to our app
    Makes sense, but get this: 12 hours after revocation, Google stops sending the error message in their response: “error_description” : “Token has been revoked.”
    It's rather misleading because you'll assume that the error message is there at all times which is not the case. You can check whether your app still has access at the apps permission page.

    用户已经主动撤销对我们应用程序的访问,这是有意义的,但是可以得到:撤销12小时后,谷歌停止发送错误消息,在他们的响应中:“error_description”:“Token已被撤销。”这是相当具有误导性的,因为您将假定错误消息一直存在,但事实并非如此。你可以检查你的应用程序在应用程序许可页面上是否还有访问权限。

  2. User has reset/recovered their Google password
    In December 2015, Google changed their default behaviour so that password resets for non-Google Apps users would automatically revoke all the user's apps refresh tokens. On revocation, the error message follows the same rule as the case before, so you'll only get the "error_description" in the first 12 hours. There doesn't seem to be any way of knowing whether the user manually revoked access (intentful) or it happened because of a password reset (side-effect).

    用户在2015年12月重置了谷歌密码,谷歌改变了他们的默认行为,让非Google Apps用户的密码重置将自动撤销所有用户的应用程序刷新令牌。在撤销时,错误消息遵循与前面的情况相同的规则,因此您只会在前12小时内获得“error_description”。似乎没有办法知道用户是否手动撤销了访问(intentful),还是因为密码重置(副作用)而导致的。

Apart from those, there's a myriad of other potential causes that could trigger the error:

除了这些,还有无数其他潜在的原因可能引发错误:

  1. Server clock/time is out of sync
  2. 服务器时钟/时间不同步
  3. Not authorized for offline access
  4. 未授权离线访问。
  5. Throttled by Google
  6. 扼杀了谷歌
  7. Using expired refresh tokens
  8. 使用刷新令牌到期
  9. User has been inactive for 6 months
  10. 用户已停用6个月
  11. Use service worker email instead of client ID
  12. 使用服务人员电子邮件而不是客户ID。
  13. Too many access tokens in short time
  14. 在短时间内太多的访问令牌。
  15. Client SDK might be outdated
  16. 客户端SDK可能已经过时了。
  17. Incorrect/incomplete refresh token
  18. 不正确的/不完整的刷新令牌

I've written a short article summarizing each item with some debugging guidance to help find the culprit. Hope it helps.

我已经写了一篇简短的文章,总结了每个条目,并提供了一些调试指导,以帮助查找罪魁祸首。希望它可以帮助。

#4


7  

I encountered the same problem. For me, I fixed this by using Email Address (the string that ends with ...@developer.gserviceaccount.com) instead of Client ID for client_id parameter value. The naming set by Google is confusing here.

我遇到了同样的问题。对于我来说,我通过使用电子邮件地址(以…@developer.gserviceaccount.com结尾的字符串)来解决这个问题,而不是使用client_id参数值的客户机ID。谷歌的命名是令人费解的。

#5


2  

I had the same error message 'invalid_grant' and it was because the authResult['code'] send from client side javascript was not received correctly on the server.

我有相同的错误消息“invalid_grant”,这是因为服务器上没有正确地接收到来自客户端javascript的authResult['code']。

Try to output it back from the server to see if it is correct and not an empty string.

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

#6


2  

My issue was that I used this URL:

我的问题是我使用了这个URL:

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

When I should have used this URL:

当我应该使用这个URL:

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

This was testing a service account which wanted offline access to the Storage engine.

这是在测试一个希望离线访问存储引擎的服务帐户。

#7


1  

if you are using scribe library, just set up the offline mode, like bonkydog suggested here is the code:

如果您正在使用scriptlibrary,只需设置离线模式,就像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/

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

#8


1  

in this site console.developers.google.com

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

this console board select your project input the oath url. the oauth callback url will redirect when the oauth success

控制台选择您的项目输入誓言url。oauth回调url将在oauth成功时重定向

#9


1  

Using a Android clientId (no client_secret) I was getting the following error response:

使用Android clientId(无client_secret),我得到了以下错误响应:

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

I cannot find any documentation for the field 'code_verifier' but I discovered if you set it to equal values in both the authorization and token requests it will remove this error. I'm not sure what the intended value should be or if it should be secure. It has some minimum length (16? characters) but I found setting to null also works.

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

I am using AppAuth for the authorization request in my Android client which has a setCodeVerifier() function.

我在Android客户机中使用AppAuth进行授权请求,它具有setCodeVerifier()函数。

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

Here is an example token request in node:

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

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

I tested and this works with both https://www.googleapis.com/oauth2/v4/token and https://accounts.google.com/o/oauth2/token.

我测试了它,它可以使用https://www.googleapis.com/oauth2/v4/token和https://accounts.google.com/o/oauth2/token。

If you are using GoogleAuthorizationCodeTokenRequest instead:

如果您使用的是GoogleAuthorizationCodeTokenRequest而不是:

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

#10


1  

This is a silly answer, but the problem for me was that I failed to realize I already had been issued an active oAuth token for my google user which I failed to store. The solution in this case is to go to the api console and reset the client secret.

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

There are numerous other answers on SO to this effect for example Reset Client Secret OAuth2 - Do clients need to re-grant access?

关于这个问题还有很多其他的答案,例如重置客户端秘密OAuth2——客户端是否需要重新授予访问权限?

#11


1  

You might have to remove a stale/invalid OAuth response.

您可能需要删除陈旧/无效的OAuth响应。

Credit: node.js google oauth2 sample stopped working invalid_grant

信贷:节点。js谷歌oauth2样本停止工作,invalid_grant

Note: An OAuth response will also become invalid if the password used in the initial authorization has been changed.

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

If in a bash environment, you can use the following to remove the stale response:

如果在bash环境中,您可以使用以下内容来删除陈旧的响应:

rm /Users/<username>/.credentials/<authorization.json>

rm /用户/ <用户名> / .credentials / < authorization.json >

#12


1  

There are two major reasons for invalid_grant error which you have to take care prior to the POST request for Refresh Token and Access Token.

存在invalid_grant错误的两个主要原因,您必须在对Refresh Token和Access Token的POST请求之前注意这些原因。

  1. Request header must contain "content-type: application/x-www-form-urlencoded"
  2. 请求头必须包含“内容类型:应用程序/x-www-form- urlencodes”
  3. Your request payload should be url encoded Form Data, don't send as json object.
  4. 请求有效负载应该是url编码的表单数据,不要以json对象发送。

RFC 6749 OAuth 2.0 defined invalid_grant as: The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.

RFC 6749 OAuth 2.0将invalid_grant定义为:所提供的授权授予(例如,授权码、资源所有者凭证)或refresh token无效、过期、撤销、不匹配授权请求中使用的重定向URI,或被分发给另一个客户端。

I found another good article, here you will find many other reasons for this error.

我发现了另一篇好文章,在这里你会发现许多其他的原因导致这个错误。

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

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

#13


0  

After considering and trying all of the other ways here, here's how I solved the issue in nodejs with the googleapis module in conjunction with the request module, which I used to fetch the tokens instead of the provided getToken() method:

在考虑并尝试了这里的所有其他方法之后,下面是我如何使用nodejs和googleapis模块与request模块一起解决这个问题的方法。

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

I simply use request to make the api request via HTTP as described here: https://developers.google.com/identity/protocols/OAuth2WebServer#offline

我只是使用请求通过这里描述的HTTP发出api请求:https://developers.google.com/identity/protocols/OAuth2WebServer#脱机

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

#14


0  

Try change your url for requst to

尝试更改requst的url

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