Access a community of over 900,000 Shopify Merchants and Partners and engage in meaningful conversations with your peers.
Hi guys, I have a PHP code:
$jwtArr = array_combine(['header', 'payload', 'signature'], explode('.', explode(" ", $_SERVER['HTTP_AUTHORIZATION'])[1]));
$calculatedHash = hash("sha256", $jwtArr['header'] . '.' . $jwtArr['payload']); //hashing two joined arguments
$calculatedHash = hash_hmac('sha256', $calculatedHash, SECRET_KEY, true); //hmac the result
$calculatedHash = base64_encode($calculatedHash); //base64 encode
echo $calculatedHash . PHP_EOL; //result
echo $jwtArr['signature']; //signature from the session token
Its been written based on this instruction: https://shopify.dev/tutorials/authenticate-your-app-using-session-tokens#verify-the-signature
But the result never the same as the signature, and I can't get in mind what am I doing wrong. Please help!
Nevermind. $calculatedHash = hash("sha256", $jwtArr['header'] . '.' . $jwtArr['payload']); - this one wasn't needed. I just wrong understand the instruction.
If anyone needs ready PHP solution:
function verify_session_token($token){
$jwtArr = array_combine(['header', 'payload', 'signature'], explode('.', $token)); //convert token to associative array
$calculatedHash = hash_hmac('sha256', $jwtArr['header'] . '.' . $jwtArr['payload'], SECRET_KEY, true); //hmac the two first arguments
$calculatedHash = rtrim(strtr(base64_encode($calculatedHash), '+/', '-_'), '='); //base64 url encode, trim =
return $calculatedHash === $jwtArr['signature'];
}
Can you please share code for this $token variable?
How can I get that $token variable data from Shopify so I can pass in above function to verify.
On front-end site you get token using AppBridge:
var utils=window['app-bridge-utils'];
utils.getSessionToken(app)
.then(res=>sessionToken=res);
and then using it making authenticated requests, by attaching it to headers of the request
So we have to do two authentication, this is so confusing?
1. Auth.
$shared_secret = "";
$params = $_GET;
$hmac = $_GET['hmac'];
$params = array_diff_key($params, array('hmac' => ''));
ksort($params);
$computed_hmac = hash_hmac('sha256', http_build_query($params), $shared_secret);
if ( hash_equals($hmac, $computed_hmac) ) {
} else {
}
2. Authenticate an embedded app using session tokens
Which PHP JWT library you are using to verify the payload? Or only verifying signature?
When JWT token will expire after 1 minute then what to do to handle this thing in our Apps. Can you please provide step by step details for how to integrate JWT tokens part in our APP?
FYI: My APP using PHP technology.
Thank you for this, but note this only verifies the signature. You still need to verify the payload. Here is how I've done that.
const CLIENT_ID = '';
const CLIENT_SECRET = '';
function verifyJWT($token) {
//verify signature
$jwtArr = array_combine(['header', 'payload', 'signature'], explode('.', $token)); //convert token to associative array
$calculatedHash = hash_hmac('sha256', $jwtArr['header'] . '.' . $jwtArr['payload'], CLIENT_SECRET, true); //hmac the two first arguments
$calculatedHash = rtrim(strtr(base64_encode($calculatedHash), '+/', '-_'), '='); //base64 url encode, trim =
$signature_verified = $calculatedHash === $jwtArr['signature'];
if(!$signature_verified) return false;
$payload = json_decode(base64_decode($jwtArr['payload']));
//verify not before time
if(time() < $payload->nbf) return false;
//verify expiration
if($payload->exp < time()) return false;
//make sure shopify domains exist in each place
if(!strstr($payload->iss, $payload->dest)) return false;
//verify AUD matches our key
if($payload->aud != CLIENT_ID) return false;
return true;
}
hi @dmogil
I followed your instruction and generated the jwt token in the promiseResult. How will I extract the token from the promiseResult?
I have pasted below the console.log(sessionToken) result.
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbW9kYXBwc3Rlc3RzdG9yZS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbW9kYXBwc3Rlc3RzdG9yZS5teXNob3BpZnkuY29tIiwiYXVkIjoiMDdiZjZmODFlMWIyZjZmMWRiZDQ3OTA0MWVjMjhkOTMiLCJzdWIiOiI0NjUzOTQ0MDI2MSIsImV4cCI6MTYzNDczNTEyMCwibmJmIjoxNjM0NzM1MDYwLCJpYXQiOjE2MzQ3MzUwNjAsImp0aSI6ImQzMjU1NDAxLWY0YjQtNGE2MC04M2FlLWI1NzlhZThiZjhiZSIsInNpZCI6ImFkYzk3YjhlNTY1YmVkODA4NmIxMjA2OGExZTVlNGU4NjQxZWQzZDMxMWZlZTE4YzI1NTVhODI2NjU2OTk5YzMifQ.r-aS0IUSS5w91b_00dqteSt2SFNWzcpZVJgWs886Oi4"
getSessionToken(app)
.then((token) => {
console.log(token);
});
yeah I used console.log to get the token alone.
How do I pass this token to my php?
@dmogil @ParanoidAndroid You mentioned to add this block to pass the session token in headers.
My code looks like this
let sessionToken = getSessionToken(app)
.then((res) => sessionToken=res);
let opts={
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer: "+sessionToken
}
};
Where do I check for the headers in my front end?