PHP Google API OAuth2 JWT请求新的访问令牌说" invalid_grant"

时间:2016-06-28 14:55:48

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

简而言之,我想在Google API上使用服务帐户获取新的访问令牌。我使用了OAuth 2.0和JWT请求。我找到了很多类似的帖子,但没有人回答我的问题。我做的一切都符合Google OAuth 2.0 Server-to-Server request guide,但是当我发送POST请求时,我得到了回报" invalid_grant"。有什么想法吗?这是我的代码:

$jwt_header_array =     array(  "alg"       => "RS256",
                                    "typ"       => "JWT");

    $jwt_claim_array =      array(  "iss"       => "upload-file@feedking-1355.iam.gserviceaccount.com",
                                    "scope"     => "https://www.googleapis.com/auth/drive.file",
                                    "aud"       => "https://www.googleapis.com/oauth2/v4/token",
                                    "exp"       => time()+3600,
                                    "iat"       => time());

    $jwt_header = base64_encode(json_encode($jwt_header_array));
    $jwt_claim = base64_encode(json_encode($jwt_claim_array));

    $key = "-----BEGIN PRIVATE KEY----- privat_key_downloaded_from_google_console -----END PRIVATE KEY-----\n";

    $pkeyid = openssl_pkey_get_private($key);
    openssl_sign($jwt_header.'.'.$jwt_claim, $jwt_signature, $pkeyid, "sha256");
    $jwt_signature = base64_encode($jwt_signature);

    $jwt = $jwt_header.'.'.$jwt_claim.'.'.$jwt_signature;

    echo $jwt.'<br /><br />';

    $url = 'https://www.googleapis.com/oauth2/v4/token';
    $query = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion='.$jwt;

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
    $result = curl_exec($ch);

    var_dump($result);

2 个答案:

答案 0 :(得分:1)

我找到了一个解决方案,这是非常明显的。为什么不使用Google Utils,Auth和Signer / P12课程?您可以使用composer(google / apiclient)获得整个软件包,除此之外您还需要这个用于进行Google API身份验证(实际上我已经有了这个软件包,愚蠢的我)。我将指导您完成整个过程:

新解决方案:

您将需要Google API套餐和Google服务帐户,如下面的旧解决方案(第1,2,3阶段)所述。从这里只需复制粘贴完全基于API的代码并替换输入数据:

$client_email = 'file-upload-final@feedking-1355.iam.gserviceaccount.com';
$private_key = file_get_contents('p12_final.p12');
$scopes = array('https://www.googleapis.com/auth/drive.file');
$credentials = new Google_Auth_AssertionCredentials(
    $client_email,
    $scopes,
    $private_key
);

$client = new Google_Client();
$client->setAssertionCredentials($credentials);
if ($client->getAuth()->isAccessTokenExpired()) {
     $client->getAuth()->refreshTokenWithAssertion();
}

OLD SOLUTION:

  1. 创建Google Service account,因为这对于在未经用户同意的情况下访问Google API至关重要。确保在创建它时,检查&#34;提供新的私钥&#34;和&#34; P12&#34;选项。如果需要,也请检查其他选项。

  2. 下载,将P12文件上传到您的服务器/本地,然后存储密码(通常是#34; notasecret&#34;此时......)。

  3. 如果您还没有使用composer或GIT安装Google API包。我更喜欢作曲家,因为GIT版本缺少一些库,对我来说Client类有点儿错误(当然可能是我的错)。

  4. 您只需使用正确的类来编写和编码JWT标头和playload,并签署证书。请注意P12证书路径以及playload中的相关输入(您可以从Google Developer Console Credetials页面获取所有这些信息,并且您还可以在Google OAuth 2.0 Server-to-Server Application页面上获取有关值的信息)。这是代码:

  5. $header = array("typ" => "JWT", 
                    "alg" => "RS256");
    
    $playload = array(  "iss"       => "upload-file2@feedking-1355.iam.gserviceaccount.com", 
                        "scope"     => "https://www.googleapis.com/auth/drive.file",
                        "aud"       => "https://www.googleapis.com/oauth2/v4/token",
                        "exp"       => time()+3600,
                        "iat"       => time());
    
    $segments = array();
    $segments[] = Google_Utils::urlSafeB64Encode(json_encode($header));
    $segments[] = Google_Utils::urlSafeB64Encode(json_encode($playload));
    $signing_input = implode(".", $segments);
    $signer = new Google_Signer_P12(file_get_contents('p12.p12', true), 'notasecret');
    $signature = $signer->sign($signing_input);
    $segments[] = Google_Utils::urlSafeB64Encode($signature);
    $jwt = implode(".", $segments);
    
    1. 使用cURL或任何您想要获取访问令牌的请求发送请求:
    2. $url = 'https://www.googleapis.com/oauth2/v4/token';
      $query = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion='.$jwt;
      
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
      $result = curl_exec($ch);
      
      1. 最后,var_dump($ result)输出应该类似:
      2. string(141)&#34; {&#34; access_token&#34;:&#34; ya29.Ci8QAzKz-U83RiaylDKgSDgH9XEVQrdBAQ5EBxMFUncN7WLfeGan0BCMJRdFJykC-g&#34;,&#34; token_type&#34;:&#34; Bearer& #34;,&#34; expires_in&#34;:3600}&#34;

        希望你喜欢它并且会赞成,因为我用这个认证故事搞砸了2天。在某些情况下,Google API可以发挥魔力,因此在inmho中学习它是必不可少的。

答案 1 :(得分:1)

您的代码中只有一个小问题,它会完美运行……原生 php base64_encode() 它与 API 预期的 base64_url_encode() 略有不同

代码如下:

function base64_url_encode(string $input) : string
{
    return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}

一切都会好起来的。

我知道已经有很多年了(从问题开始)并且 Google 创建了可以通过 Composer 轻松下载和安装的库,但是…… 就我而言,我需要在过去 7 天内从 GA 获得访问。使用 Google 的原始库,我可以做到。但它有 66.8 MB 的文件,只是为了解决这个小问题。

我知道也有很多有用的库,但我不需要它们。我只需要在过去 7 天内从 GA 访问。这就是我可以用原生 PHP 和 OpenSSL 函数做的事情……大约 1 kB 的代码。