OAuth2 JWT请求新的访问令牌为“invalid_grant”


oauth 2.0 - PHP Google API OAuth2 JWT request for new acces token says "invalid_grant"

总之,我想获得一个新的访问令牌与谷歌API上的服务帐户。我使用了OAuth 2.0和JWT请求。我发现了很多类似的帖子,但没有一个回答我的问题。我所做的一切都符合谷歌OAuth 2.0服务器到服务器请求指南,但当我发送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);

我已经找到了一个解决方案,而且它非常明显。为什么不使用谷歌Utils, Auth和签名者/P12类?你可以用composer (google/apiclient)获得整个包,除此之外,你还需要这个来制作google API认证(事实上我已经有了这个包,真傻)。我将指导您完成整个过程:

新解决方案:

您将需要Google API包和Google Service帐户,如下面的旧解决方案(阶段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();
}

旧的解决方案:

  1. 创建一个谷歌服务帐户,因为这是必不可少的访问谷歌api无需进一步的用户同意。请确保在创建时勾选"提供新私钥"answers"P12"选项。如果需要,请检查其他选项

  2. 下载并上传P12文件到您的服务器/本地,并存储密码(此时通常为"notasecret"…)

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

  4. 您只需要使用正确的类来制作和编码JWT头和播放加载,并签署证书。注意P12证书路径和playload中的当前输入(您可以从Google Developer Console credentials页面获得所有这些信息,也可以在Google OAuth 2.0 Server-to-Server Application页面获得有关值的信息)。代码如下:

$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);
  • 发送请求与cURL或任何你想要的访问令牌:
  • $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)的输出应该类似于

    字符串(141){"access_token": "ya29. "Ci8QAzKz-U83RiaylDKgSDgH9XEVQrdBAQ5EBxMFUncN7WLfeGan0BCMJRdFJykC-g", "token_type": "Bearer", "expires_in": 3600} "

    希望你喜欢它,并会投票,因为我搞砸了2天这个认证的故事。Google API在某些情况下可以创造奇迹,所以学习它是必不可少的。

    你的代码中只有一个小问题,它将完美地工作…原生php base64_encode()它与API期望的base64_url_encode()有点不同

    代码是这样的:

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

    ,一切都将正常运行。

    我知道这已经有几年了(从问题),谷歌创建了可以很容易下载的库,Composer安装,但是…在我的情况下,我需要从GA得到最后7天的访问。有了谷歌的原始库,我就能做到。但这是66.8 MB的文件,只是为了这个小问题。

    我知道也有很多有用的库,但是我不需要它们。我只需要过去7天GA的访问。这就是我可以用本地PHP和OpenSSL函数来做的…大约1kb的代码。