Google api 试图从google获取oAuth令牌的\u授权无效
在尝试从Google获取oAuth令牌以连接到他们的联系人api时,我不断收到一个Google api 试图从google获取oAuth令牌的\u授权无效,google-api,Google Api,在尝试从Google获取oAuth令牌以连接到他们的联系人api时,我不断收到一个无效的\u grant错误。所有的信息都是正确的,我让tripple检查了这一点 有人知道是什么导致了这个问题吗?我尝试过为它设置不同的客户端id,但得到了相同的结果,我尝试过连接许多不同的方法,包括尝试强制身份验证,但结果仍然相同。当我将用户发送到OAuth时没有显式请求“脱机”访问时,我遇到了这个问题“是否要授予此应用程序触摸您的物品的权限?”页面 请确保在请求中指定access\u type=offline
无效的\u grant
错误。所有的信息都是正确的,我让tripple检查了这一点
有人知道是什么导致了这个问题吗?我尝试过为它设置不同的客户端id,但得到了相同的结果,我尝试过连接许多不同的方法,包括尝试强制身份验证,但结果仍然相同。当我将用户发送到OAuth时没有显式请求“脱机”访问时,我遇到了这个问题“是否要授予此应用程序触摸您的物品的权限?”页面 请确保在请求中指定access\u type=offline 详情如下:
(另外:我认为谷歌在2011年末增加了这一限制。如果你有以前的旧代币,你需要将你的用户发送到权限页面以授权脱机使用。)尽管指定了“脱机”选项,我还是遇到了同样的问题“
根据bonkydog的回答,在我的请求中输入。长话短说,我发现这里描述的解决方案对我有效:
本质上,当您在Google API的控制台中添加OAuth2客户端时,Google将为您提供“客户端ID”和“电子邮件地址”(假设您选择“webapp”作为客户端类型)。尽管谷歌有误导性的命名约定,但他们希望您在访问OAuth2 API时,将“电子邮件地址”作为client\u id
参数的值发送
这适用于调用以下两个URL:
请注意,如果您使用“客户ID”而不是“电子邮件地址”调用第一个URL,则会成功调用该URL。但是,当尝试从第二个URL获取承载令牌时,使用该请求返回的代码将不起作用。相反,您将收到一条“错误400”和一条“无效授权”消息 我收到了相同的错误消息“invalid_grant”,这是因为
authResult['code']
服务器上未正确接收从客户端发送的javascript
尝试从服务器输出它,看看它是否正确,而不是空字符串。我遇到了同样的问题。对于我来说,我通过使用电子邮件地址(以...@developer.gserviceaccount.com结尾的字符串)而不是客户机ID作为客户机ID参数值来解决这个问题。Google设置的命名在这里很混乱。如果您使用的是scribe库,只需设置脱机模式,就像bonkydog建议的那样
代码如下:
OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
.callback(callbackUrl).scope(SCOPE).offline(true)
.build();
我的问题是我使用了这个URL:
https://accounts.google.com/o/oauth2/token
https://www.googleapis.com/oauth2/v4/token
当我应该使用此URL时:
https://accounts.google.com/o/oauth2/token
https://www.googleapis.com/oauth2/v4/token
这是在测试一个服务帐户,该帐户希望脱机访问此站点中的。
此控制台板选择您的项目并输入宣誓url。
oauth回调url将在oauth成功时重定向虽然这是一个老问题,但似乎许多人仍然会遇到它-我们自己花了几天时间来跟踪这个问题
在OAuth2规范中,“invalid_grant”是与无效/过期/吊销令牌(auth grant或refresh令牌)相关的所有错误的总括
对我们来说,问题有两个方面:
用户已主动撤销对我们的应用程序的访问权
这是有道理的,但要明白:撤销12小时后,谷歌停止在其回复中发送错误消息:
“错误描述”:“令牌已被撤销。”
这是相当误导的,因为您会假设错误消息始终存在,而事实并非如此。您可以检查您的应用程序是否仍有访问权限
用户已重置/恢复其谷歌密码
2015年12月,他们的默认行为,即非谷歌应用程序用户的密码重置将自动撤销所有用户的应用程序刷新令牌。撤销时,错误消息遵循与之前相同的规则,因此您只能在前12小时内获得“错误描述”。似乎没有任何方法可以知道用户是手动撤销访问(有意的),还是因为密码重置(副作用)而发生的
除此之外,还有无数其他可能引发错误的潜在原因:
服务器时钟/时间不同步
未授权脱机访问
被谷歌扼杀
使用过期的刷新令牌
用户已停用6个月
使用服务人员电子邮件而不是客户ID
短时间内访问令牌过多
客户端SDK可能已过时
刷新令牌不正确/不完整
总结每一项,并提供一些调试指导,以帮助找到罪魁祸首。希望能有所帮助。在考虑并尝试了这里的所有其他方法之后,下面是我如何使用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请求,如下所述:
尝试将requst的url更改为
https://www.googleapis.com/oauth2/v4/token
使用Android客户端ID(无客户端密码),我得到以下错误响应:
{
"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();
下面是一个令牌请求示例
final GoogleAuthorizationCodeTokenRequest req = new GoogleAuthorizationCodeTokenRequest(
TRANSPORT,
JSON_FACTORY,
getClientId(),
getClientSecret(),
code,
redirectUrl
);
req.set("code_verifier", null);
GoogleTokenResponse response = req.execute();
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();
{"error": "redirect_uri_mismatch", "error_description": "Bad Request"}
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)
$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;
$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;