invalid_grant
我在尝试从 Google 获取 oAuth 令牌以连接到他们的联系人 api 时不断收到错误消息。所有的信息都是正确的,我已经检查了三次,这让我很困惑。
有谁知道可能导致此问题的原因?我尝试为其设置不同的客户端 ID,但得到相同的结果,我尝试连接许多不同的方式,包括尝试强制身份验证,但结果仍然相同。
invalid_grant
我在尝试从 Google 获取 oAuth 令牌以连接到他们的联系人 api 时不断收到错误消息。所有的信息都是正确的,我已经检查了三次,这让我很困惑。
有谁知道可能导致此问题的原因?我尝试为其设置不同的客户端 ID,但得到相同的结果,我尝试连接许多不同的方式,包括尝试强制身份验证,但结果仍然相同。
access_type
尽管根据 bonkydog 的回答在我的请求中指定了“离线”,但我遇到了同样的问题。长话短说,我发现这里描述的解决方案对我有用:
https://groups.google.com/forum/#!topic/google-analytics-data-export-api/4uNaJtquxCs
本质上,当您在 Google API 的控制台中添加 OAuth2 客户端时,Google 会给您一个“客户端 ID”和一个“电子邮件地址”(假设您选择“webapp”作为您的客户端类型)。client_id
尽管 Google 有误导性的命名约定,但他们希望您在访问他们的 OAuth2 API 时 发送“电子邮件地址”作为参数的值。
这适用于调用这两个 URL 时:
请注意,如果您使用“客户 ID”而不是“电子邮件地址”调用第一个 URL,则调用该 URL 将成功。但是,当尝试从第二个 URL 获取不记名令牌时,使用从该请求返回的代码将不起作用。相反,您将收到“错误 400”和“invalid_grant”消息。
当我在将用户发送到 OAuth 时没有明确请求“离线”访问时遇到了这个问题“你想授予这个应用程序触摸你的东西的权限吗?” 页。
确保在请求中指定 access_type=offline。
此处的详细信息:https ://developers.google.com/accounts/docs/OAuth2WebServer#offline
(另外:我认为 Google 在 2011 年末添加了此限制。如果您有在那之前的旧令牌,您需要将您的用户发送到权限页面以授权离线使用。)
If you're testing this out in postman / insomnia and are just trying to get it working, hint: the server auth code (code parameter) is only good once. Meaning if you stuff up any of the other parameters in the request and get back a 400, you'll need to use a new server auth code or you'll just get another 400.
我遇到了同样的问题。对我来说,我通过使用电子邮件地址(以 ...@developer.gserviceaccount.com 结尾的字符串)而不是 client_id 参数值的客户端 ID 来解决此问题。谷歌设置的命名在这里令人困惑。
我的问题是我使用了这个 URL:
https://accounts.google.com/o/oauth2/token
当我应该使用这个 URL 时:
https://www.googleapis.com/oauth2/v4/token
这是在测试一个希望离线访问存储引擎的服务帐户。
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.
There are numerous other answers on SO to this effect for example Reset Client Secret OAuth2 - Do clients need to re-grant access?
Using a Android clientId (no client_secret) I was getting the following error response:
{
"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.
I am using AppAuth for the authorization request in my Android client which has a setCodeVerifier()
function.
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
.
If you are using GoogleAuthorizationCodeTokenRequest
instead:
final GoogleAuthorizationCodeTokenRequest req = new GoogleAuthorizationCodeTokenRequest(
TRANSPORT,
JSON_FACTORY,
getClientId(),
getClientSecret(),
code,
redirectUrl
);
req.set("code_verifier", null);
GoogleTokenResponse response = req.execute();
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.
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.
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
Solved by removing all Authorized redirect URIs in Google console for the project. I use server side flow when you use 'postmessage' as redirect URI
We tried so many things, and in the end the issue was that the client had turned "Less Secure App Access" off in their Google Account settings.
To turn this on:
I hope this saves someone some time!
我有相同的错误消息“invalid_grant”,这是因为 服务器上未正确接收从客户端 javascript 发送的authResult['code'] 。
尝试从服务器输出它,看看它是否正确,而不是一个空字符串。
Try change your url for requst to
https://www.googleapis.com/oauth2/v4/token
You might have to remove a stale/invalid OAuth response.
Credit: node.js google oauth2 sample stopped working invalid_grant
Note: An OAuth response will also become invalid if the password used in the initial authorization has been changed.
If in a bash environment, you can use the following to remove the stale response:
rm /Users/<username>/.credentials/<authorization.json>
如果您正在使用 scribe 库,例如设置离线模式,如 bonkydog 建议的那样。这是代码:
OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
.callback(callbackUrl).scope(SCOPE).offline(true)
.build();
The code you obtain in the URL after user consent has a very short expiry. Please obtain the code again and attempt to get access token within seconds (you have to hurry) and it should work. I can't find out the expiry period of code but it's literally very short.
In my case it was a callback URL that was different from the original request. So, callback URL should be the same for auth request and code exchange.
在这个网站 console.developers.google.com
此控制台板选择您的项目输入誓言网址。oauth 成功时,oauth 回调 url 将重定向
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:
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
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
For future folks... I read many articles and blogs but had luck with solution below...
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 blog depicts different cases in which "invalid_grant" error comes.
Enjoy!!!
for me I had to make sure that the redirect_uri
is an exact match to the one in the developer console Authorised redirect URIs
, that fixed it for me, I was able to debug and know what exactly was the issue after switching from
https://accounts.google.com/o/oauth2/token
to https://www.googleapis.com/oauth2/v4/token
I got a proper error:
{"error": "redirect_uri_mismatch", "error_description": "Bad Request"}
I had this problem after enabling a new service API on the Google console and trying to use the previously made credentials.
To fix the problem, I had to go back to the credential page, clicking on the credential name, and clicking "Save" again. After that, I could authenticate just fine.
In my case, the issue was in my code. Mistakenly I've tried to initiate client 2 times with the same tokens. If none of the answers above helped make sure you do not generate 2 instances of the client.
My code before the fix:
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)
as soon as I change it to (use only one instance):
@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)
it fixed my issues with grant type.
For me the issues was I had multiple clients in my project and I am pretty sure this is perfectly alright, but I deleted all the client for that project and created a new one and all started working for me ( Got this idea fro WP_SMTP plugin help support forum) I am not able to find out that link for reference
If you are sanitizing user input (For example, $_GET["code"]
in php) Make sure you don't accidentally replace something in the code.
The regex I am using is now /[^A-Za-z0-9\/-]/
Look at this https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988
First you need an 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;
Safe the Access Token and the Refresh Token and the expire_in, in a Database. The Access Token expires after $expires_in seconds. Than you need to grab a new Access Token (and safe it in the Database) with the following Request:
$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;
Bear in Mind to add the redirect_uri Domain to your Domains in your Google Console: https://console.cloud.google.com/apis/credentials in the Tab "OAuth 2.0-Client-IDs". There you find also your Client-ID and Client-Secret.
There is a undocumented timeout between when you first redirect the user to the google authentication page (and get back a code), and when you take the returned code and post it to the token url. It works fine for me with the actual google supplied client_id as opposed to an "undocumented email address". I just needed to start the process again.
For me, this was caused by subsequent getToken
calls with the same code.
Namely, in NestJS my callback endpoint was decorated with @UseGuards(AuthGuard('google'))
and I tried to call getToken
in the callback.
I my case I just didn't read the documentation properly because I was trying to do const { tokens } = await oauth2Client.getToken(accessToken);
every time to get an authorized client instance but for the subsequent requests you only need to include the refresh_token
you store after the first user auth.
oauth2Client.setCredentials({
refresh_token: `STORED_REFRESH_TOKEN`
});