假设我在交换从/token
端点(/auth
)获得的代码后,从Google的OAuth2 using this example OAuth Playground request端点得到以下响应:
{
"access_token": "ya29.eQETFbFOkAs8nWHcmYXKwEi0Zz46NfsrUU_KuQLOLTwWS40y6Fb99aVzEXC0U14m61lcPMIr1hEIBA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1/ZagesePFconRc9yQbPxw2m1CnXZ5MNnni91GHxuHm-A",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJhODc0MjBlY2YxNGU5MzRmOWY5MDRhMDE0NzY4MTMyMDNiMzk5NGIifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwMTY5NDg0NDc0Mzg2Mjc2MzM0IiwiYXpwIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXRfaGFzaCI6ImFVQWtKRy11Nng0UlRXdUlMV3ktQ0EiLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJpYXQiOjE0MzIwODI4NzgsImV4cCI6MTQzMjA4NjQ3OH0.xSwhf4KvEztFFhVj4YdgKFOC8aPEoLAAZcXDWIh6YBXpfjzfnwYhaQgsmCofzOl53yirpbj5h7Om5570yzlUziP5TYNIqrA3Nyaj60-ZyXY2JMIBWYYMr3SRyhXdW0Dp71tZ5IaxMFlS8fc0MhSx55ZNrCV-3qmkTLeTTY1_4Jc"
}
如何对访问令牌进行哈希处理,以便将其与ID令牌的at_hash
声明进行比较?
我可以在服务器上本地验证ID令牌以防止客户端修改,并且想要验证访问令牌是使用id令牌发出的那个(暗示受众和主题与ID令牌匹配)。
at_hash
ID令牌声称是OpenID Connect的defined:
访问令牌哈希值。它的值是access_token值的ASCII表示的八位字节的最左半部分的base64url编码,其中使用的散列算法是ID令牌的JOSE标题的alg Header参数中使用的散列算法。例如,如果alg是RS256,则使用SHA-256对access_token值进行散列,然后取最左边的128位并对其进行base64url编码。 at_hash值是区分大小写的字符串。
c_hash
ID Token对混合流的声明是defined similarly,相同的步骤可用于验证。
从令牌生成at_hash
或c_hash
的步骤:
alg
来哈希令牌的ASCII表示,在Google的情况下使用SHA-256。下面是python中用于创建该哈希的一些示例代码,您需要两个库,pycrypto
和google-api-python-client
(对于base64编码和id令牌比较,您可以替代替代)。您可以像这样安装pip:
pip install pycrypto
pip install --upgrade google-api-python-client
然后,以交互方式运行python
,并尝试以下操作:
# Config: app's client id & tokens (in this case OAuth Playground's client id, and the tokens were extracted from the Token Endpoint response).
client_id = "407408718192.apps.googleusercontent.com"
id_token_string = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjcwZjZjNDI2NzkyNWIzMzEzNmExZDFjZmVlNGViYzU3YjI0OWU1Y2IifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXRfaGFzaCI6Iml5VkFfTnNtY2JJMDFHcFJDQVJaOEEiLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTAxNjk0ODQ0NzQzODYyNzYzMzQiLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJpYXQiOjE0NjcyMTg1NzMsImV4cCI6MTQ2NzIyMjE3M30.e4hJJYeUaFVwJ9OC8LBnmOjwZln_E2-isEUJtb-Um7vt3GDZnBZkHdCokAPBL4OW3DXBNPk9iY0QL2P5Gpb-nX_s-PZKOIES8CE0i2DmGahCZgJY_Y3V2qwiP1fTEQjcUmHEG2e7OdCn6siSZveFQ0W7SiSbbSeJVLws9aoHROo_UXy8CVjaU5KinROG6m6igqCxFoskIWRzAynfx70xMadY4UdS8kbKK_v5id0_Rdg_gYlF1ND0lsPM9vdm3jOifQEAAkjHr-RuSDWlX4Bs4cQtEkeQkN6--MWhoqAshJITuGSazVIiDkVUNNBIXmB_dp9TO6ZjeQEEfeGCs6axKA"
access_token = "ya29.Ci8QA5eGBdBglK59FXdqXIR5KnbMJs-swx6Alk6_AV_6YPkjhxdO1e0Hqxi-8NB3Ww"
# Verifies & parses id token.
idtoken = oauth2client.client.verify_id_token(id_token_string, client_id)
# Token to hash & expected hash value (replace with code & c_hash to verify code).
token_to_hash = access_token
token_hash_expected = idtoken["at_hash"]
# Step 1. hashes the access token using SHA-256 (Google uses `RS256` as the ID Token `alg`).
hash = hashlib.sha256()
hash.update(token_to_hash)
digest = hash.digest() # this returns the hash digest bytes (not a hex string)
# Step 2. truncates the hash digest to the first half.
digest_truncated = digest[:(len(digest)/2)]
# Step 3. base64url encodes the truncated hash digest bytes.
token_hash_computed = oauth2client.crypt._urlsafe_b64encode(digest_truncated)
# Compares computed to expected, outputs result.
str("Computed at_hash: %s" % token_hash_computed)
str(token_hash_computed == token_hash_expected)
要使用您自己帐户中的新ID令牌尝试此样本,请使用带有OAuth Playground范围的profile
创建请求(或使用this one),交换代码以进行刷新和访问令牌,并将响应复制到上面示例中的token_response_http_body
(删除换行符)。
PHP解决方案:
$accessToken = 'xxx';
$idToken = 'yyy';
$client = new Google_Client();
$verification = $client->verifyIdToken($idToken);
$hash = hash('sha256', $accessToken);
$hash = substr($hash, 0, 32);
$hash = hex2bin($hash);
$hash = base64_encode($hash);
$hash = rtrim($hash, '=');
$hash = str_replace('/', '_', $hash);
$hash = str_replace('+', '-', $hash);
if ($hash === $verification['at_hash']) {
// access token is valid
}
Google_Client
在这里:https://packagist.org/packages/google/apiclient