I need to get an access token for my service account, that was created in my google console to get private access for google spreadsheet service!
My php file for testing:
<?php
function base64_url_encode($input) {
return strtr(str_replace('=', '', base64_encode($input)), '+/', '-_');
}
header('Content-type: text/plain');
$header = '{"alg":"RS256","typ":"JWT"}';
$payload =
'{
"iss":"[my service account]@[my project name].iam.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/spreadsheets",
"aud":"https://www.googleapis.com/oauth2/v4/token",
"exp":'.(time() + 3600).',
"iat":'.(time()).'
}';
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$private_key = "-----BEGIN PRIVATE KEY-----[my private key from google console json file of my service account]-----END PRIVATE KEY-----\n";
//$rsa->setPassword('password');
$rsa->loadKey($private_key); // private key
$plaintext = base64_url_encode($header).'.'.base64_url_encode($payload);
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
//$rsa->loadKey($rsa->getPublicKey()); // public key
//echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
$jwt = base64_url_encode($header).'.'.base64_url_encode($payload).'.'.base64_url_encode($signature);
$query = http_build_query(Array('grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt));
//echo $query;
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => $query
)
);
$context = stream_context_create($options);
$access_token = file_get_contents('https://www.googleapis.com/oauth2/v4/token', false, $context);
?>
The php output:
file_get_contents(https://www.googleapis.com/oauth2/v4/token) [<a href='function.file-get-contents'>function.file-get-contents</a>]: failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request
If I run the same query in wfetch ("$query" variable from php script):
HTTP/1.1 400 Bad Request\r\n
{\n
"error": "invalid_grant",\n
"error_description": "Invalid JWT Signature."\n
}\n
P.S. My php version is PHP 5.2 (without of namespace support), also phpseclib1.0.3 used.
P.P.S. Tryed to change RS256 to HS256 and google still response 400 - Invalid JWT Signature, but if change to nonexistent XX256 (for example) -> google response 500 - error: internal_failure. So I think may be google also supports HS256. If it's true - I can use mutch more simple HS256 method. And I found this: https://github.com/progrium/php-jwt/blob/master/JWT.php
Here's a complete example with bare openssl/curl:
<?php
$url = "https://www.googleapis.com/oauth2/v4/token";
$scope = 'https://www.googleapis.com/auth/spreadsheets';
$client_id = '[email protected]';
$p12 = 'google-api-xxxxx.p12';
$p12pwd = 'notasecret';
function base64_url_encode($input) {
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
$iat = time();
$jwt_data = array(
'iss' => $client_id,
'aud' => $url,
'scope' => $scope,
'exp' => $iat + 3600,
'iat' => $iat,
);
openssl_pkcs12_read(file_get_contents($p12), $certs, $p12pwd);
$header = array('typ' => 'JWT', 'alg' => 'RS256');
$signing_input = base64_url_encode(json_encode($header)) . '.' . base64_url_encode(json_encode($jwt_data));
openssl_sign($signing_input, $signature, $certs['pkey'], 'SHA256');
$jwt = $signing_input . '.' . base64_url_encode($signature);
$data = array(
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion" => $jwt
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch) ;
echo $response;
?>