如何使用id令牌的at_hash声明来验证访问令牌?

问题描述 投票:3回答:2

假设我在交换从/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令牌匹配)。

google-oauth google-openid
2个回答
11
投票

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_hashc_hash的步骤:

  1. 使用与ID令牌本身相同的alg来哈希令牌的ASCII表示,在Google的情况下使用SHA-256。
  2. 将哈希截断到原始哈希值的前半部分(重要的是:不是哈希的字符串十六进制表示)。
  3. Base64url编码(不填充)截断的散列字节。

下面是python中用于创建该哈希的一些示例代码,您需要两个库,pycryptogoogle-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(删除换行符)。


1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.