Session token verification issue

dmogil
New Member
8 0 0

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!

Replies 13 (13)
dmogil
New Member
8 0 0

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'];
	}
Jorem
New Member
3 0 0

Can you please share code for this $token variable?

dmogil
New Member
8 0 0

Not sure what you mean

Jorem
New Member
3 0 0

How can I get that $token variable data from Shopify so I can pass in above function to verify.

dmogil
New Member
8 0 0

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

let opts={
method: "POST",
    headers: { 
    "Content-Type": "application/json",
    "Authorization": "Bearer: "+sessionToken
    }
};
Then you get it from the header on server side and use above function to verify it
Jorem
New Member
3 0 0

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

Saifullah_Bin_A
Tourist
8 0 2

Which PHP JWT library you are using to verify the payload? Or only verifying signature?

darakhsa_farhan
New Member
11 0 0

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.

ParanoidAndroid
Tourist
6 1 0

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;
}

 

 

priyankatotalav
New Member
5 0 0

hi @dmogil 

 

I followed your instruction and generated the jwt token in the promiseResult. How will I extract the token from the promiseResult?

const getSessionToken = AppBridgeUtil.getSessionToken;
 let sessionToken = getSessionToken(app).then(res=>sessionToken=res);   
console.log(sessionToken);

I have pasted below the console.log(sessionToken) result.

Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbW9kYXBwc3Rlc3RzdG9yZS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbW9kYXBwc3Rlc3RzdG9yZS5teXNob3BpZnkuY29tIiwiYXVkIjoiMDdiZjZmODFlMWIyZjZmMWRiZDQ3OTA0MWVjMjhkOTMiLCJzdWIiOiI0NjUzOTQ0MDI2MSIsImV4cCI6MTYzNDczNTEyMCwibmJmIjoxNjM0NzM1MDYwLCJpYXQiOjE2MzQ3MzUwNjAsImp0aSI6ImQzMjU1NDAxLWY0YjQtNGE2MC04M2FlLWI1NzlhZThiZjhiZSIsInNpZCI6ImFkYzk3YjhlNTY1YmVkODA4NmIxMjA2OGExZTVlNGU4NjQxZWQzZDMxMWZlZTE4YzI1NTVhODI2NjU2OTk5YzMifQ.r-aS0IUSS5w91b_00dqteSt2SFNWzcpZVJgWs886Oi4"
ParanoidAndroid
Tourist
6 1 0
getSessionToken(app)
.then((token) => {
console.log(token);
});

 

priyankatotalav
New Member
5 0 0

yeah I used console.log to get the token alone.

How do I pass this token to my php?

 

priyankatotalav
New Member
5 0 0

@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?