dreamevil0002 2016-06-28 14:55
浏览 243

PHP Google API OAuth2 JWT对新访问令牌的请求显示“invalid_grant”

In short, I want to obtain a new acces token with a service account on Google API. I used OAuth 2.0 and a JWT request. I found many similar post, but non of them answered my question. I done everything conform to the Google OAuth 2.0 Server-to-Server request guide, but when i send the POST request, i get in return "invalid_grant". Any ideas why? Here is my code:

$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-----
";

    $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);
  • 写回答

1条回答 默认 最新

  • douju4594 2016-06-29 09:00
    关注

    I've found a solution, and it was ridiculously obvious. Why not using Google Utils, Auth and Signer/P12 classes? You can get the whole package with composer (google/apiclient), beside you will need this for making Google API authentication (in fact i already had this package, dumb me). I will guide you through the whole process:

    NEW SOLUTION:

    You will need the Google API package and the Google Service account as described in the old solution below (stage 1, 2, 3). From here just copy-paste the fully API based code and replace the input datas:

    $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. Create a Google Service account, since this is essential to access Google APIs without further user consent. Make sure that when you create it, you check "Furnish new private key" and "P12" options. Check other options as well if needed.

    2. Download, upload the P12 file to your server / local, and store the password (its usually "notasecret" at this time...).

    3. Install the Google API package with composer or GIT, if you don't have already. I prefer composer because the GIT version lacks some libraries, for me the Client class was a bit buggy (could be my fault of course).

    4. You just have to use the right classes to make and encode the JWT header and playload, and sign the certificate. Take care of the P12 certificate path and for the corrent inputs in the playload (you can get all of it from the Google Developer Console Credetials page, and you can also get informations about values on Google OAuth 2.0 Server-to-Server Application page). Here is the code:

    $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. Send the request with cURL or whatever you want to get the Access token:
    $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. Finally the var_dump($result) output should be something similar:

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

    Hope you enjoyed it and will upvote, since i messed up 2 days with this authentication story. Google API can do magic in some cases, so it's an essential thing to learn inmho.

    评论

报告相同问题?

悬赏问题

  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
  • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计
  • ¥70 PlayWright在Java上连接CDP关联本地Chrome启动失败,貌似是Windows端口转发问题
  • ¥15 帮我写一个c++工程
  • ¥30 Eclipse官网打不开,官网首页进不去,显示无法访问此页面,求解决方法
  • ¥15 关于smbclient 库的使用
  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?