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

Wil*_*iss 3 google-openid google-oauth

假设/token在交换从/auth端点获取的代码后,我从Google的OAuth2 端点获得以下响应(使用此示例OAuth Playground请求):

{
  "access_token": "ya29.eQETFbFOkAs8nWHcmYXKwEi0Zz46NfsrUU_KuQLOLTwWS40y6Fb99aVzEXC0U14m61lcPMIr1hEIBA", 
  "token_type": "Bearer", 
  "expires_in": 3600, 
  "refresh_token": "1/ZagesePFconRc9yQbPxw2m1CnXZ5MNnni91GHxuHm-A", 
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJhODc0MjBlY2YxNGU5MzRmOWY5MDRhMDE0NzY4MTMyMDNiMzk5NGIifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwMTY5NDg0NDc0Mzg2Mjc2MzM0IiwiYXpwIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXRfaGFzaCI6ImFVQWtKRy11Nng0UlRXdUlMV3ktQ0EiLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJpYXQiOjE0MzIwODI4NzgsImV4cCI6MTQzMjA4NjQ3OH0.xSwhf4KvEztFFhVj4YdgKFOC8aPEoLAAZcXDWIh6YBXpfjzfnwYhaQgsmCofzOl53yirpbj5h7Om5570yzlUziP5TYNIqrA3Nyaj60-ZyXY2JMIBWYYMr3SRyhXdW0Dp71tZ5IaxMFlS8fc0MhSx55ZNrCV-3qmkTLeTTY1_4Jc"
}
Run Code Online (Sandbox Code Playgroud)

如何散列访问令牌以便将其at_hash与ID令牌的声明进行比较?

我可以在服务器上本地验证ID令牌以防止客户端修改,并且想要验证访问令牌是使用id令牌发出的那个(暗示受众和主题与ID令牌匹配).

Wil*_*iss 12

at_hashID令牌权利要求被限定通过ID连接这样:

访问令牌哈希值.它的值是access_token值的ASCII表示的八位字节的最左半部分的base64url编码,其中使用的散列算法是ID令牌的JOSE标题的alg Header参数中使用的散列算法.例如,如果alg是RS256,则使用SHA-256对access_token值进行散列,然后取最左边的128位并对其进行base64url编码.at_hash值是区分大小写的字符串.

c_hash混合流的ID令牌声明的定义类似,可以使用相同的步骤来验证.

生成at_hashc_hash来自令牌的步骤:

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

下面是python中用于创建该哈希的一些示例代码,您需要两个库,pycrypto并且google-api-python-client(对于base64编码和id令牌比较,您可以替代替代).您可以像这样安装pip:

pip install pycrypto
pip install --upgrade google-api-python-client
Run Code Online (Sandbox Code Playgroud)

然后,以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)
Run Code Online (Sandbox Code Playgroud)

要使用您自己帐户中的新ID令牌尝试此示例,请使用带示波器的OAuth Playground创建请求profile(或使用token_response_http_body示例),交换代码以进行刷新和访问令牌,并将响应复制到上面的示例中(删除换行符).