0

I am trying to obtain a JWT Bearer Token from Salesforce. I am trying to follow their documentation but am however unsuccessful. https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5

The return i am getting is {"error":"invalid_grant","error_description":"invalid assertion"}

Here is my code not sure what is wrong with this. Need help trying to get a bearer token returned.

Thank You and help is appreciated.

<?php

/**
 * Encode data to Base64URL
 * @param string $data
 * @return boolean|string
 */
function base64url_encode($data)
{
    return strtr(base64_encode($data), '+/', '-_');
}

define('CONSUMER_KEY', 'abc123');
define('CONSUMER_SECRET', 'abc123secret');
define('LOGIN_BASE_URL', 'https://login.salesforce.com');

//Json Header
$headerArray = array(
    "alg" => "RS256"
);

$jsonHeader = json_encode(($headerArray));
$header = base64url_encode($jsonHeader);

//Create JSon Claim/Payload
$expiration = strval(time() + (5 * 60));

$claimSet = array(
    "iss" => CONSUMER_KEY,
    "sub" => "test@email.com",
    "aud" => LOGIN_BASE_URL,
    "exp" => $expiration
);
$jsonClaimSet = (json_encode($claimSet));
$payload = base64url_encode($jsonClaimSet);

$encoded_JWT = $header . '.' . $payload;

// LOAD YOUR PRIVATE KEY FROM A FILE - BE CAREFUL TO PROTECT IT USING
$private_key = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
testtesttesttest
-----END RSA PRIVATE KEY-----
EOD;

// This is where openssl_sign will put the signature
$signature = "";
// SHA256 in this context is actually RSA with SHA256
$algo = "SHA256";
// Sign the header and payload
openssl_sign($encoded_JWT, $signature, $private_key, $algo);


// Base64 encode the result
$secret = base64url_encode($signature);

$token = $header . '.' . $payload . '.' . $secret;

$token_url =  LOGIN_BASE_URL.'/services/oauth2/token';

$post_fields = array(
    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    'assertion' => $token
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $token_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
// curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type : application/x-www-form-urlencoded"));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

$token_request_body = curl_exec($ch)
or die("Call to get token from code failed: '$token_url' - ".print_r($post_fields, true));

print_r($token_request_body);
?>
0

Some notes:

  • You probably want to be using str_replace (replace all instances) rather than strstr (find first instance, no replacement)
  • Check that the JSON for your claims set is a JSON Object (e.g. {"iss":"x","aud":"y","sub":"z","expr":12345}), not an array (like [{"iss":"x"},{"aud":"y"},{"sub":"z"},{"expr":12345}])
  • The header also needs to be Base64URL encoded
  • The connected app's consumer secret isn't used in the JWT flow, and you should probably delete it from your code to reduce the chance that it gets leaked

Base64URL needs all instances of "+" and "/" replaced with "-" and "_" (respectively)

Adding some more prints to your code (like how we'd add system.debug() in apex) will help in debugging. You can (and probably should) print out the results of your various steps (i.e. print the serialized header, print the serialized claims, print the concatenation, print the signed copy of the concatenated bits, etc...)

The general structure of the JWT assertion is

Base64URL(header) 
+ '.' + Base64URL(claims)
+ '.' + (Base64URL(Encrypt(Base64URL(header) + '.' + Base64URL(claims)))
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.