Say I have the following response from Google\'s OAuth2 /token
endpoint after exchanging the code obtained from the /auth
endpoint (using this example
I am going to give an answer based on OpenID Connect Core specification (read here). Looking at section 3.2.2.9, a client can validate an access token that was given by an authorization server with an ID Token.
The steps are as follows:
Step 1 requires the client to know what algorithm was used to sign the ID Token. It can be done by decoding the ID Token and checking the Header section for the alg property. Let's say the alg is equal to RS256 then, the hash algorithm used to create at_hash is SHA-256. If it was RS384 then the hash algorithm is SHA-384, and so on, you get the point.
Step 2 requires halving the hashed value, and take the left half to apply base64url encoding.
Step 3 then expects the at_hash value in the ID Token to be equal to the hash operations done in step 1 & 2. If it's not equal, then the access token wasn't issued with the specified ID Token.
A PHP implementation would roughly be like this:
public function verifyToken($id_token, $access_token)
{
$header = $this->decodeJWT($id_token, 0);
$claims = $this->decodeJWT($id_token, 1);
return $this->createAtHash($access_token, $header['alg']) === $claims['at_hash'];
}
public function decodeJWT($jwt, $section = 0)
{
$parts = explode(".", $jwt);
return json_decode(base64url_decode($parts[$section]));
}
public function createAtHash($access_token, $alg)
{
// maps HS256 and RS256 to sha256, etc.
$hash_algorithm = 'sha' . substr($alg, 2);
$hash = hash($hash_algorithm, $access_token, true);
$at_hash = substr($hash, 0, strlen($hash) / 2);
return $this->urlSafeB64Encode($at_hash);
}
public function urlSafeB64Encode($data)
{
$b64 = base64_encode($data);
$b64 = str_replace(array('+', '/', "\r", "\n", '='),
array('-', '_'),
$b64);
return $b64;
}
Call verifyToken
passing your ID Token and access token. It will return true if the hash matches, and false on the contrary.