我正在尝试连接到需要OAuth 2.0身份验证的Google API。第一步要求我使用以下格式生成JSON Web令牌(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授权服务器支持的唯一签名算法是使用SHA-256哈希算法的RSA。在alg字段中表示为RS256 JWT标题。“
“使用SHA256withRSA(也称为带有SHA-256哈希函数的RSASSA-PKCS1-V1_5-SIGN)使用从Google Developers Console获得的私钥对输入的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”错误的任何想法?
答案 0 :(得分:0)
你有两个问题:
第一个: 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]'));
答案 1 :(得分:0)
你收到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
断言的字段不是来自凭据的ClientID。这是电子邮件地址。
3
时间应该是UTC,所以你应该
$time = gmmktime();
4
如前所述,@ florent-morselli需要签名。这是真的。
5
检查机器的时间。同步时间非常重要
在此之后一切都会好的,谷歌会给你一个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