Google API OAuth 2.0 身份验证返回“invalid_grant” - 可能的私钥签名格式问题?
Posted
技术标签:
【中文标题】Google API OAuth 2.0 身份验证返回“invalid_grant” - 可能的私钥签名格式问题?【英文标题】:Google API OAuth 2.0 authentication returning "invalid_grant" - possible private key signature format issue? 【发布时间】:2014-11-19 07:55:02 【问题描述】:我正在尝试连接到需要 OAuth 2.0 身份验证的 Google API。第一步需要我生成一个 JSON Web Token (JWT):https://developers.google.com/accounts/docs/OAuth2ServiceAccount?hl=ru#creatingjwt,格式如下:
Base64url encoded header.
Base64url encoded claim set.
Base64url encoded signature
我按照文档中的所有说明进行操作,但继续收到“invalid_grant”错误。我怀疑这与最后(签名)部分有关。根据文档:
"计算签名时必须使用JWT头中的签名算法。Google OAuth 2.0授权服务器唯一支持的签名算法是RSA使用SHA-256哈希算法。这在alg字段中表示为RS256 JWT 标头。”
“使用从 Google Developers Console 获得的私钥,使用 SHA256withRSA(也称为带有 SHA-256 哈希函数的 RSASSA-PKCS1-V1_5-SIGN)对输入的 UTF-8 表示进行签名。输出将是字节数组。”
“签名必须是 Base64url 编码的。”
我直接从 Google Dev Console 复制了私钥(包括“BEGIN PRIVATE KEY”部分)并对其进行了 base64 编码。这是我正在使用的代码:
$time = time();
$url = 'https://accounts.google.com/o/oauth2/token';
$assertion = base64_encode('"alg":"RS256","typ":"JWT"').'.';
$assertion .= base64_encode('
"iss":"'.$this->configs['client_id'].'",
"scope":"https://www.googleapis.com/auth/youtube",
"aud":"'.$url.'",
"exp":'.($time+3600).',
"iat":'.$time.'
').'.';
$assertion .= base64_encode('[-----BEGIN PRIVATE KEY-----privateKeyHere-----END PRIVATE KEY-----\n]');
$headers = array(
'Host: accounts.google.com',
'Content-Type: application/x-www-form-urlencoded'
);
$fields = array(
'grant_type' => urlencode('urn:ietf:params:oauth:grant-type:jwt-bearer'),
'assertion' => urlencode($assertion)
);
$fields_string = '';
foreach($fields as $key => $value) $fields_string .= '&'.$key.'='.$value;
$fields_string = substr($fields_string, 1);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_HEADER, TRUE );
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1 );
$result = curl_exec($ch);
curl_close($ch);
var_dump($result);
关于为什么返回“invalid_grant”错误的任何想法?
【问题讨论】:
【参考方案1】:你有两个问题:
第一个:PHP 函数 base64_encode 不是 JWT 规范所要求的 URL 安全。 要获取 Base64Url 字符串,请使用以下函数:
function base64url_encode($data)
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
将您的 base64_encode
呼叫替换为 base64url_encode
第二个:您永远不会签署您的数据!您只需添加您的私钥,而不是标头和声明集的签名。 使用以下函数:
function sign($input, $private_key)
$signature = null;
openssl_sign($input, $signature, $private_key, "SHA256");
return $signature;
然后替换
$assertion .= base64_encode('
"iss":"'.$this->configs['client_id'].'",
"scope":"https://www.googleapis.com/auth/youtube",
"aud":"'.$url.'",
"exp":'.($time+3600).',
"iat":'.$time.'
').'.';
$assertion .= base64_encode('[-----BEGIN PRIVATE KEY-----privateKeyHere-----END PRIVATE KEY-----\n]');
与
$assertion .= base64_encode('
"iss":"'.$this->configs['client_id'].'",
"scope":"https://www.googleapis.com/auth/youtube",
"aud":"'.$url.'",
"exp":'.($time+3600).',
"iat":'.$time.'
');
$assertion .= '.'.base64url_encode(sign($assertion, '[-----BEGIN PRIVATE KEY-----privateKeyHere-----END PRIVATE KEY-----\n]'));
【讨论】:
【参考方案2】:您收到 invalid_grant 是因为您这样做了
1.
'grant_type' => urlencode('urn:ietf:params:oauth:grant-type:jwt-bearer')
把它改成只是
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'
2.
iss 断言字段不是来自凭据的 ClientID。这是电子邮件地址。
3.
时间应该是UTC,所以你应该这样做
$time = gmmktime();
4.
如@florent-morselli 所述,您需要签名。这是真的。
5.
检查您机器的时间。时间同步很重要
之后一切都会好起来的,Google 会给你一个 access_token。
顺便说一句:
privateKey should be without []\n characters (In my case this is lines of 64 symbols)
CURLOPT_POST takes only true or false
http_build_query is better way to build queryString
'Host: accounts.google.com' in $headers is useless, because you set CURLOPT_URL
【讨论】:
以上是关于Google API OAuth 2.0 身份验证返回“invalid_grant” - 可能的私钥签名格式问题?的主要内容,如果未能解决你的问题,请参考以下文章
带有 Google Analytics API v3 的 OAuth 2.0
通过 OAuth 2.0 自动使用 google-api-dotnet-client
使用 Laravel API 实现 OAuth 2.0 身份验证