尝试从Google获取OAuth令牌的_授权无效

在尝试从Google获取OAuth令牌以连接到其联系人API时,我不断收到invalid_grant错误。所有的信息都是正确的,我已经检查了三次,所以有点被难住了。

有人知道是什么导致了这个问题吗?我已经尝试为它设置不同的客户端ID,但我得到相同的结果,我已经尝试连接许多不同的方式,包括尝试强制身份验证,但仍然相同的结果。

341214 次浏览

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

请确保在请求中指定访问_type=offline.

详情如下:https://developers.google.com/accounts/docs/oauth2webserver#offline

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

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

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

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

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

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

请注意,如果您使用“客户ID ”而不是“电子邮件地址”调用第一个URL,则对该URL的调用将成功。但是,当尝试从第二个URL获取承载令牌时,使用从该请求返回的代码将不起作用。相反,您将收到“错误400 ”和“无效的_授权”消息。

我有同样的错误消息'无效的_授予',这是因为 AuthResult['代码'] 服务器上未正确接收到来自客户端JavaScript的发送。

尝试将其从服务器输出回来,以查看它是否正确并且不是空字符串。

我遇到了同样的问题。我通过使用电子邮件地址(以..@developer.gserviceaccount.com结尾的字符串)而不是客户机ID作为客户机_ID参数值解决了这个问题。谷歌的命名设置在这里令人困惑。

如果您正在使用Scribe Library,例如设置离线模式,就像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://accounts.google.com/o/oauth2/token

当我应该使用此URL时:

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

这是在测试想要脱机访问存储引擎的服务帐户。

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

这个控制台板选择你的项目,输入OATH URL. 当OAuth成功

时,OAuth回调URL将重定向

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

在OAuth2规范中,“无效_授权”是与无效/过期/撤销令牌(身份验证授权或刷新令牌)相关的所有错误的总称。

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

  1. 用户已主动撤销对我们应用程序的访问权限
    有道理,但得到这个:吊销12小时后,_ABC在其响应中_0错误消息: “error_description” : “Token has been revoked.”
    这是相当具有误导性的,因为您会假设错误消息一直存在,但事实并非如此。您可以在应用程序权限页面中检查您的应用是否仍具有访问权限。

  2. 用户已重置/恢复其Google密码
    2015年12月,_ABC_其默认行为,因此非谷歌应用程序用户的密码重置将自动撤销所有用户的应用程序刷新令牌。在撤销时,错误消息遵循与前一种情况相同的规则,因此在最初的12小时内,您只能获得“错误_描述”。似乎没有任何方法可以知道用户是手动撤销访问权限(有意)还是因为密码重置(副作用)。

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

  1. 服务器时钟/时间不同步
  2. 未授权脱机访问
  3. 被谷歌扼杀了
  4. 使用过期的刷新令牌
  5. 用户已处于非活动状态6个月
  6. 使用服务人员电子邮件而不是客户ID
  7. 短时间内访问令牌过多
  8. 客户端SDK可能已过时
  9. 刷新令牌不正确/不完整

我写了一篇短文。对每个项目进行了总结,并提供了一些调试指导,以帮助找到问题所在。希望能有所帮助。

在考虑并尝试了所有其他方法之后,下面是我如何在NodeJS中使用googleapis模块和request模块来解决该问题,我使用该模块来获取令牌,而不是使用提供的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请求,如下所述: 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

尝试将REQUST的URL更改为

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

使用Android ClientID(无客户端_密码)时,我收到以下错误响应:

{
"error": "invalid_grant",
"error_description": "Missing 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();

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

关于这一点,

还有许多其他的答案,例如 重置客户端密码OAuth2-客户端是否需要重新授予访问权限?

您可能必须删除过时/无效的OAuth响应。

贷:Node.JS Google OAuth2示例已停止工作无效的_授权

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

如果在bash环境中,您可以使用以下命令删除过时的响应:

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

_授权无效错误有两个主要原因,您必须在POST请求刷新令牌和访问令牌之前加以注意。

  1. 请求头必须包含";内容类型:application/X-www-form-urlencoded";
  2. 您的请求负载应该是URL编码的表单数据,不要作为JSON对象发送。

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

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

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

为了未来的人们..我读了很多文章和博客,但幸运地找到了下面的解决方案……

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

博客描述了出现“无效的_授权”错误的不同情况。

享受吧!

我来说,我必须确保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"}

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

要解决此问题,我必须返回到凭据页面,单击凭据名称,然后单击“保存”。在那之后,我可以很好地鉴定。

在我的例子中,问题出在我的代码中。我错误地尝试使用相同的令牌启动客户端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)

它解决了我的格兰特类型的问题。

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

如果您正在清理用户输入(例如,PHP中的$_GET["code"]),请确保您没有意外地替换代码中的某些内容。

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

看这个https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988

首先,您需要一个访问_令牌:

$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;

在数据库中安全访问令牌、刷新令牌和过期_。访问令牌在$Expires_后过期(以秒为单位)。然后,您需要通过以下请求获取新的访问令牌(并将其保存在数据库中):

$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;

请记住,将重定向_URI域添加到Google控制台中的域中:在“ OAuth 2.0-Client-Ids ”选项卡中https://console.cloud.google.com/apis/credentials。在那里你还可以找到你的客户ID和客户密码。

在您第一次将用户重定向到Google身份验证页面(并返回代码)和获取返回的代码并将其发布到令牌URL之间有一个未记录的超时。对我来说,使用Google提供的实际客户端_ID,而不是“未记录的电子邮件地址”,它工作得很好。我只是需要重新开始这个过程。

如果您正在Postman/Insomnia中测试它,并且只是想让它工作,提示:服务器身份验证代码(代码参数)只有一次是好的。这意味着,如果您在请求中塞满了任何其他参数并得到400,您将需要使用新的服务器身份验证代码,或者您将得到另一个400。

通过删除项目的Google控制台中的所有授权重定向URI

来解决。 当您使用“ postMessage ”作为重定向URI

时,我使用服务器端流程

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

我们尝试了很多东西,最后的问题是客户转向了 ";较不安全的应用程序访问";在他们的谷歌账户设置中。

要启用此功能:

  1. 转到https://myaccount.google.com/并管理帐户
  2. 转到“安全”选项卡
  3. 打开安全性较低的应用程序访问

enter image description here

我希望这能节省一些时间!

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

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

在我的例子中,它是一个与原始请求不同的回调URL. 因此,对于身份验证请求和代码交换,回调URL应相同

在我的情况下,我只是没有正确读取文档,因为我正在尝试执行const { tokens } = await oauth2Client.getToken(accessToken); 每次获取授权客户端实例,但对于后续请求,您只需要包含_ABC,_在第一次用户身份验证后存储的0。

oauth2Client.setCredentials({
refresh_token: `STORED_REFRESH_TOKEN`
});

对我来说,在测试Google Connect链接API之前,我必须删除Web cookie.

如果您的重定向_URL与您在Google Gloud上创建令牌时的URL不同,则可能会发生这种情况。所以确保它是正确的。

在以下位置创建新的凭据密钥:

enter image description here

是给我的蜂蜜。

从:

enter image description here

向:

enter image description here

没有一个答案对我起作用。打电话之后

https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=<CALLBACK_URL_YOU_SET_ON_GOOGLE>&prompt=consent&response_type=code&client_id=<YOUR CLIENT ID>&scope=openid%20email%20profile&access_type=offline

我得到的响应URL返回,其中有code值。

4%2F0ARtbsJoGfJYFT6Cy1VpPiONgYV1vIzVhGtTK9MvyQLZfuGv_sAPDRDOzqGkRu07qOIxBfg

没有解码收到的URL,而是复制了这个值,并将其传递给我的应用程序API,并获得了报告的错误,invalid_grant
上述代码中的%2F应替换为/。应该是的

4/0ARtbsJoGfJYFT6Cy1VpPiONgYV1vIzVhGtTK9MvyQLZfuGv_sAPDRDOzqGkRu07qOIxBfg

请注意,此code仅起作用一次。

教训:始终解码收到的URL!