Google API OAuth 2.0身份验证返回“invalid_grant” - 可能是私钥签名格式问题?

时间:2014-09-24 17:54:38

标签: php oauth-2.0 google-api google-oauth jwt

我正在尝试连接到需要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”错误的任何想法?

2 个答案:

答案 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