I am having trouble creating a JWT for Google Drive authorization.
The instruction is given in this link.
<?php
//helper function
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
//Google's Documentation of Creating a JWT https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests
$raw_key_data="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkWhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCLJN7B2USp0bTH\n7aVt1K/pAJa9rgUEkebbRQUPEopManZHgJK8SMzq5nUM3OOVlTdyDyuzxUZW65Y+\nR1xajNHSidE+rJJ3MWwuevXBuUws5oiA+DXJBB6FsTtHDvUPT7sePbJx8Y80S1Jr\nB3apJNdohjSef4UihgjJ93A0PgeSVz7CeUKxrBHdGiK2Ikf7wTCzcRzz/15I6QLu\ncsNbWxo0+koXo9D0+wT3RN+riSyTa2WwYn//B8v6VmDPVrcjxETEXraAIMpqURtK\n4BuK4Qp2pSd1u2zrGc58BVGuT6+nPVpmX6wiZ21sqOulTzgIOXKIuc4zdKvZ+jz/\n1jst3ertAgMBAAECggEADRGn+IYbLGYdeD/Kb2/wG87p2aP8Jas8hzDK4lkH81h2\nho29eoDN+mwt50jh+V08CXMCVE69phFXmb7jHkAmvwMhy6Sy1w4lzpHO/mSUko0O\nmip2BszjvwPgAPMXMlp3RUZfOdOJ80v10Eaxrv5eWxtr2s04aH81WR7sA4Ql+uki\nbTiLbo4odpMVPkoJZATZKQd+L/bBM4a+b3IM87/TE7skMrSgQE6cnjjTI5Uk+WSl\nxjiZvj5XmticW3vPavL/ZXPEZqy5IxvcxdF5rGHCHVu4ah0CmDdc9A3jF0flhewK\nV6mOViqbKGInnMn4kt3l4C3+wF2+dge6t8BQ/TMtwwKBgQDBnpjiC+wRVQy36KCs\ntYdZ60ykuQeKqMyACt6FvC74xy+PjLVntxDaYArva2PVaZSm9B2FgGHPz9AfW80/\n1ZeNHcuwymni8n+4MNpQbJuhuHK8UjSo0rFq6Nrddi9RCjzASSX8s4Dlm01VM65b\nKBnM5p50I9QEY4F4gSZfLLMNowKBgQC3+Tpz23qhkxJPu9cVuvPRFfg1y0NNsp7k\n0qHYMegz1GHoMVs1kg36OtbROqpG2+rIrj0JWTGwLO14fX2TY0jWfao21CGrpkXM\nlY1KSDIMuQv6pd5yh74oqvDDpZwKxxu/nmzcQbd1lN/nFkEW5g0b7e27UoCoovwS\n7qSENbqOLwKBgHYWp8H+aX1stPQZ8p1DngiupTE2FK5yIz/Y4T0JuFBNE+nmdOGL\n2sCFoUXC5sHLwjlNXBAHbCCV66akk/th5yvPR2NNIOWk51bMnOo+Q3GQEJJhRPLO\nhhzhZlN5+IPhzYmtU3jbdjsTzEex3J6GR64b3fqRu4bttZJsmp2jopUnAoGAWW+7\nzt7/+tR4rnJu2Y2NQjQf+mbaTUddb2kWbPe2Hpw9DJgR8zURvngkPor6hIAc33p1\nCbpmwXLV7yFyjthRbJSizwzJYZzvicmaamY2jqDXBf7k6WC8PSD88t/rwAGTp8/o\ntBruiSbawoi7E9q6vL0qOUqeaVzylnGVYQCNtNkCgYBwqL1MNTR8IrXDfZyYdDRP\nWNCRqm7ymuQi7IUKVa+yaBM1ubvEe7EPrlZWlFPDdPmaScx02Qwf++xcGHWzzDX0\nQPmd95OTGafvECXuKVy2NAf2AdCYVruL+17wfPhuz7ANIpgEqsiNAZNe0GtGBjyZ\nVuiSVVML3jW4XUtf63V0/A==\n-----END PRIVATE KEY-----\n";
$private_key = openssl_get_privatekey($raw_key_data);
if ($private_key === FALSE)
{
error_log ("Unable to extract private key from raw key data: " . openssl_error_string());
};
print("$private_key\n");
//{Base64url encoded JSON header}
$jwtHeader = base64url_encode(json_encode(array(
"alg" => "RS256",
"typ" => "JWT"
)));
//{Base64url encoded JSON claim set}
$now = time();
$jwtClaim = base64url_encode(json_encode(array(
"iss" => "myService-account-id#mySite.iam.gserviceaccount.com",
"scope" => "https://www.googleapis.com/auth/drive.file",
"aud" => "https://www.googleapis.com/oauth2/v4/token",
"exp" => $now + 3600,
"iat" => $now
)));
//The base string for the signature: {Base64url encoded JSON header}. {Base64url encoded JSON claim set}
openssl_sign(
$jwtHeader.".".$jwtClaim,
$jwtSig,
$private_key,
"SHA256"
);
$jwtSign = base64url_encode($jwtSig);
//{Base64url encoded JSON header}.{Base64url encoded JSON claim set}. {Base64url encoded signature}
$jwtAssertion = $jwtHeader.".".$jwtClaim.".".$jwtSig;
print("$jwtAssertion\n");
?>
When I run this code, I get Resource id #4 followed by a nonsense signature. I even tried putting the private key in a file and reading from the file in which there is multiple lines (private key separated at \n ) but no success. I can't generate meaningful JWT signature from the private key. Can anyone help me with this issue?
Thanks,
I have managed to solve this issue. Below is the updated code that reads the JSON file downloaded from Google and creates an assertion with which one can then get access token from Google:
<?php
//helper function
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
// Read the JSON credential file my-private-key.json download from Google
$private_key_file="my-private-key.json";
$json_file = file_get_contents($private_key_file);
$info = json_decode($json_file);
$private_key = $info->{'private_key'};
//{Base64url encoded JSON header}
$jwtHeader = base64url_encode(json_encode(array(
"alg" => "RS256",
"typ" => "JWT"
)));
//{Base64url encoded JSON claim set}
$now = time();
$jwtClaim = base64url_encode(json_encode(array(
"iss" => $info->{'client_email'},
"scope" => "https://www.googleapis.com/auth/drive.file",
"aud" => "https://www.googleapis.com/oauth2/v4/token",
"exp" => $now + 3600,
"iat" => $now
)));
$data = $jwtHeader.".".$jwtClaim;
// Signature
$Sig = '';
openssl_sign($data,$Sig,$private_key,'SHA256');
$jwtSign = base64url_encode( $Sig );
//{Base64url encoded JSON header}.{Base64url encoded JSON claim set}.{Base64url encoded signature}
$jwtAssertion = $data.".".$jwtSign;
echo "$jwtAssertion\n";
You may then test your code by communicating with Google Drive for example:
curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=YOUR-JWT-ASSERTION-BUILD-ABOVE' https://www.googleapis.com/oauth2/v4/token
Related
I am trying to get the jwt token, but i am getting errors everytime with everything that i try. Below are th things that i have tried. I do get the jwt-token without the package but when i use the jwt.io to check the signature verification,it fails every time. With the package i get different errors like the private key cannot be coereced and sometimes invalid algorithm. Please correct me where i am messing it up.
Without php-Jwt
$header = [
'alg' => "RS384",
'typ' => "JWT"
];
$payload = [
'iss' => 'my-client-id',
'sub' => 'my-client-id',
'aud' => "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
'jti' => (string)strtotime(gmdate("Y-m-d H:i:s")),
'exp' => strtotime(gmdate("Y-m-d H:i:s")) + 270,
];
$privateKey = "my private Key"
$headers_encoded = $this->base64url_encode(json_encode($header));
$payload_encoded = $this->base64url_encode(json_encode($payload));
$signature = hash_hmac('sha384', $headers_encoded.'.'.$payload_encoded, $privateKey, true);
// Encode the signature as a base64url string
$signature_encoded = $this->base64url_encode($signature);
$jwt = $headers_encoded.'.'.$payload_encoded.'.'.$signature_encoded;
- With php-jwt
installed the package
composer require firebase/php-jwt --ignore-platform-req=ext-mongodb
Used the required files in my controller
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
Tried Encoding:
$check = JWT::encode(
$header,
$payload,
$privateKey,
'RS384'
);
i get different errors like the private key cannot be coereced and sometimes invalid algorithm. Please correct me where i am messing it up.
In simple words, this is what I am doing or trying to do. A generic form of my code:
<?php
// Load the private key from a file
$privateKey = file_get_contents('private.key');
// Set the header and payload for the JWT
$header = [
'alg' => 'RS384',
'typ' => 'JWT'
];
$payload = [
'sub' => '1234567890',
'name' => 'John Doe',
'iat' => 1516239022
];
// Encode the header and payload as JSON strings
$headerEncoded = base64_encode(json_encode($header));
$payloadEncoded = base64_encode(json_encode($payload));
// Concatenate the header, payload, and secret to create the signature
$signature = hash_hmac('sha384', "$headerEncoded.$payloadEncoded", $privateKey, true);
// Encode the signature as a base64 string
$signatureEncoded = base64_encode($signature);
// Concatenate the header, payload, and signature to create the JWT
$jwt = "$headerEncoded.$payloadEncoded.$signatureEncoded";
echo $jwt;
I do get the jwt-signature but on https://jwt.io/ it shows unverified.
Fixed
Use this instead of hash_mac():
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA384);
Make sure if the key is stored in PHP variable, format the key like this:
$privateKey = "------BEGIN PRIVATE KEY------\n".
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\n".
"MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\n".
.
.
.
"TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\n".
"dn/RsYEONbwQSjIfMPkvxF+8HQ==\n".
"------END PRIVATE KEY------";
enclose every line in Double quotes "" and add \n at end of every line.
How to generate JWT token in php using with the following parameters
Subject, Issuer, Expiry time and payload in the < PAYLOAD > tag.
Id can be any random number of any length.
subject is TestService
Issuer is Baguma Inc
Expiry Time will be 30 sec from current time(ideally ).
Payload is the request from Third Party
SigningKEY is fcvxcnfrhrtghkfghgwerikdf
Signature algorithm will be HS512.
Sample request from Third Party is shown below
<COMMAND><TYPE>REQUEST</TYPE><INTERFACE>TESTACCOUNT</INTERFACE> <REQUESTID>123</REQUESTID></COMMAND
Your answer got me started. Here's the working code I went with (albeit generalized from your specific case). Thanks for getting me started!
function gen_jwt():String{
$signing_key = "changeme";
$header = [
"alg" => "HS512",
"typ" => "JWT"
];
$header = base64_url_encode(json_encode($header));
$payload = [
"exp" => 0,
];
$payload = base64_url_encode(json_encode($payload));
$signature = base64_url_encode(hash_hmac('sha512', "$header.$payload", $signing_key, true));
$jwt = "$header.$payload.$signature";
return $jwt;
}
/**
* per https://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid/15875555#15875555
*/
function base64_url_encode($text):String{
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($text));
}
With the help of an article from DZone Security, I managed to generate a JWT token by doing the following
Define the base64UrlEncode function which replaces + with -, / with _ and = with ''.
function base64UrlEncode($text)
{
return str_replace(
['+', '/', '='],
['-', '_', ''],
base64_encode($text)
);
}
Encode the headers using base64UrlEncode
$headers = [ "alg" => "HS512"];
$headers_encoded = $this->base64url_encode(json_encode($headers));
Encode the Payload using Base64 URL encode as well
$issuedAt = time();
$payload = [
"id" =>$this->gen_uuid(), // .setId(UUID.randomUUID().toString())
"sub"=> "TestService", //Subject
"exp"=> $issuedAt+30,
"iss"=> "Baguma Inc", //issuer
"iat"=> $issuedAt, //issued at
"PAYLOAD"=> "<COMMAND><TYPE>REQUEST</TYPE><INTERFACE>TESTACCOUNT</INTERFACE> <REQUESTID>123</REQUESTID></COMMAND"];
$payload_encoded = $this->base64url_encode(json_encode($payload));
Using the Key/secret build the signature
$key = "fcvxcnfrhrtghkfghgwerikdf"
$signature = hash_hmac('sha512',"$headers_encoded.$payload_encoded",$key,true);
Build and return the token
$token = "$headers_encoded.$payload_encoded.$signature_encoded";
Given this code:
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
$key = 'secret';
//setting the header: 'alg' => 'HS256' indicates that this token is signed using HMAC-SHA256
$header = array(
'alg' => 'HS256',
'typ' => 'JWT'
);
// Returns the JSON representation of the header
$header = json_encode($header);
//encodes the $header with base64.
$header = base64url_encode($header);
$payload = array("a" => "b");
$payload = json_encode($payload);
$payload = base64url_encode($payload);
$signature = hash_hmac('SHA256','$header.$payload', $key, true);
$signature = base64url_encode($signature);
echo "$header.$payload.$signature";
Returns the following JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.rhCKIvkwiuNcchxDZnGak8XT1q8lmLhnm8aIxzUioWg
But the signature is not verified at https://jwt.io/
The payload is decrypted well though... What may be the problem ?
You are using single quotes around $header.payload when calculating the HMAC, rather than double quotes; the former uses the literal string and does not expand the variables:
$signature = hash_hmac('SHA256', "$header.$payload", $key, true);
Problems in access to Google API OAuth2 Service Account with google-api-php-client. What I surprised to discover is this part of code from example of a JWT claim set in Google Dev returning wrong JWT Claim set when for some reason JWT Header pass true what making me feel like UTF-8 serialization with json_encode() creating an error here.
$JWT_Header = array(
"alg" => "RS256",
"typ" => "JWT"
);
$JWT_Body = array(
"iss" => "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5#developer.gserviceaccount.com",
"scope" => "https://www.googleapis.com/auth/prediction",
"aud" => "https://accounts.google.com/o/oauth2/token",
"exp" => 1328554385,
"iat" => 1328550785
);
$segments = array(
rtrim(strtr(base64_encode(json_encode($JWT_Header)), '+/', '-_'), '='),
rtrim(strtr(base64_encode(json_encode($JWT_Body)), '+/', '-_'), '=')
);
//echo '<pre>'; print_r($segments); echo '</pre>';
different from what is described in Google Dev: Encoding the JWT Claim Set example of output
/*
Output:
JWT header:
Google Dev eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
My output eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
JWT Claim set:
Google Dev eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.
My output eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOlwvXC93d3cuZ29vZ2xlYXBpcy5jb21cL2F1dGhcL3ByZWRpY3Rpb24iLCJhdWQiOiJodHRwczpcL1wvYWNjb3VudHMuZ29vZ2xlLmNvbVwvb1wvb2F1dGgyXC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.
*/
Any suggestions?
It doesn't seem to be related to UTF-8 in any way. To debug, just apply the same algorithm backwards:
$output = array(
'eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ',
'eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOlwvXC93d3cuZ29vZ2xlYXBpcy5jb21cL2F1dGhcL3ByZWRpY3Rpb24iLCJhdWQiOiJodHRwczpcL1wvYWNjb3VudHMuZ29vZ2xlLmNvbVwvb1wvb2F1dGgyXC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ',
);
foreach($output as $i){
print_r(base64_decode(strtr($i, '-_', '+/'))) . PHP_EOL;
print_r(json_decode(base64_decode(strtr($i, '-_', '+/')))) . PHP_EOL;
}
You'll see that Google JSON string contains the same values, except that they don't escape forward slashes:
Google: https://www.googleapis.com/auth/prediction
You: https:\/\/www.googleapis.com\/auth\/prediction
Ive cracked oAuth and have my class file for it. I'm at the last stage of posting a tweet and all works except all the words are joined with a plus sign in the tweet.
Changing anything results in the signature been incorrect and twitter returns 401 error.
So how does one remove the pluses? Post function below:
function post($token, $tokenSecret, $status)
{
// Default params
$params = array(
"oauth_version" => "1.0",
"oauth_nonce" => time(),
"oauth_timestamp" => time(),
"oauth_consumer_key" => $this->key,
"oauth_signature_method" => "HMAC-SHA1",
"oauth_token" => $token,
"status" => $status
);
uksort($params, 'strcmp');
// convert params to string
foreach ($params as $k => $v) {$pairs[] = $this->_urlencode_rfc3986($k).'='.$this->_urlencode_rfc3986($v);}
$concatenatedParams = implode('&', $pairs);
// form base string (first key)
$baseString= "POST&".$this->_urlencode_rfc3986($this->request_statuses_url)."&".$this->_urlencode_rfc3986($concatenatedParams);
// form secret (second key)
$secret = $this->_urlencode_rfc3986($this->secret)."&".$this->_urlencode_rfc3986($tokenSecret);
// make signature
$sig = $this->_urlencode_rfc3986(base64_encode(hash_hmac('sha1', $baseString, $secret, TRUE)));
// BUILD URL
$url = $this->request_statuses_url; // twitter update url
$paramString = $concatenatedParams."&oauth_signature=".$sig;
// Send to cURL
$result = $this->_http($url, $paramString);
if($result['httpCode'] == '200'){
// Return array
return $result;
}
else{
// Error
show_error($result['httpCode'], $result['httpCode']);
return FALSE;
}
}
Is $status your tweet? Take a look at the POST request before you post it, my guess is _urlencode_rfc3986() converts it so that you get "$status=This+is+my+tweet" when you want "$status=This is my tweet"
Twitter is not supporting "+" as escape for spaces, which as far as I know is a violation of the standard.
You have to replace the the + with %20.