From 01e20227083ed6f7a514c5d910f15e057070fcc5 Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Mon, 27 Apr 2026 17:41:45 -0400 Subject: [PATCH 1/6] Editorial draft --- openid-connect-key-binding-1_0.md | 195 +++++++++++++++--------------- 1 file changed, 95 insertions(+), 100 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index f38dbc7..496874e 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -58,27 +58,22 @@ organization="Cloudflare" .# Abstract -OpenID Key Binding specifies how to bind a public key to an OpenID Connect ID Token using mechanisms defined in [@!RFC9449], OAuth 2.0 Demonstrating Proof of Possession (DPoP). +This specification defines how to bind a public key to an OpenID Connect ID Token using mechanisms defined in [@!RFC9449], OAuth 2.0 Demonstrating Proof of Possession (DPoP). {mainmatter} # Introduction -OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication and obtain identity claims to an OpenID Connect Provider (OP). +OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication to and obtain identity claims from an OpenID Connect Provider (OP). When authenticating with OpenID Connect an RP initiates the protocol by making an authentication request to the OP. The OP after authenticates the identity of the user and sends the RP an ID Token signed by the OP and containing claims about the user. -When authenticating with OpenID Connect, an RP provides a nonce in its authentication request. The ID Token signed and returned by the OP contains the nonce and claims about the user. When verifying the ID Token, the RP confirms it contains the nonce, binding the session that made the request to the response. +It is common for an RP to be composed of multiple components such as a RP authenticating component that obtains the ID Token from the OP and an RP consuming component which checks the ID Token presented to it by the authenticating component. When the RP authenticating component wants to prove to an RP consuming component that it has authenticated a user, it may present the ID Token as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks. For instance, if an attacker obtains the ID Token, they can impersonate the authenticated user. -It is common for an RP to be composed of multiple components such as a RP authenticating component that obtains the ID Token from the OP and an RP consuming component which checks the ID Token presented to it by the authenticating component. When the RP authenticating component wants to prove to an RP consuming component that it has authenticated a user, it may present the ID Token as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks - if an attacker obtains the ID Token, they can impersonate the authenticated user. +By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that a user has been authenticated, but that the RP authenticating component itself was the original recipient of that authentication. This provides stronger security guarantees, prevent token theft and replay attacks, by transforming the ID Token from a bearer token into a proof-of-possession token. -By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that a user has been authenticated, but that the RP authenticating component itself was the original recipient of that authentication. This transforms the ID Token from a vulnerable bearer token into a proof-of-possession token that provides stronger security guarantees. - -The RP may also prove possession of the bound key when presenting an ID Token back to the OP. - -Use cases include: a mobile app that has received an ID Token exchanging the ID Token with a proof of possession with a first party authorization service for an access token; an instance of a peer to peer application such as video conferencing where one instance of the application sends the ID Token with a proof of possession to a second instance to prove which user is operating the first instance. +Use cases include: a mobile app that has received an ID Token exchanging the ID Token with a proof-of-possession with a first party authorization service for an access token; an instance of a peer to peer application such as video conferencing where one instance of the application sends the ID Token with a proof of possession to a second instance to prove which user is operating the first instance. This specification profiles OpenID Connect 1.0 [@!OpenID.Core], RFC8628 - OAuth 2.0 Device Authorization Grant [@!RFC8628], and RFC9449 - OAuth 2.0 Demonstrating Proof of Possession (DPoP) [@!RFC9449] to enable cryptographically bound ID Tokens that resist theft and replay attacks while maintaining compatibility with existing OpenID Connect infrastructure. - ## Requirements Notation and Conventions The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", @@ -103,16 +98,21 @@ This specification uses the following terms: The parameters **dpop_jkt** and **DPoP** as defined in [@!RFC9449] -## Protocol Profile Overview +## OpenID Connect Metadata + +The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: -This specification profiles how to bind a public key to an ID Token. +- the `bound_key` scope in the `supported_scopes` +- the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] -For the Authorization Code Flow: +## Authorization Code Flow -1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request -2. receiving the authorization `code` as usual in the Authentication Response -3. adding the `DPoP` header that includes the SHA-256 hash of the `code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` -4. adding the `cnf` claim containing the public key to the returned ID Token +The Authorization Code Flow works as follows: + +1. The RP performs OpenID Connect Authentication Request but with the addition of the `bound_key` scope and a `dpop_jkt`parameter set to the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key. +2. The OP authenticates the user and responds with the authorization `code` as usual in the Authentication Response. +3. The RP makes a Token Request to the OP `token_endpoint` with a `DPoP` header that includes the SHA-256 hash of the `code`, `c_s256`, claims. +4. The OP returns an ID Token that has a the `cnf` claim containing the RP specified proof-of-possession public key ``` +------+ +------+ @@ -131,6 +131,82 @@ For the Authorization Code Flow: +------+ +------+ ``` +### Authentication Request - Authorization Code Flow + +If the RP authenticating component is running on a device that supports a web browser, it makes an authorization request per [@!OpenID.Core] 3.1. In addition to the `scope` parameter containing `openid`, and the `response_type` having the value `code`, the `scope` parameter MUST also include `bound_key`, and the request MUST include the `dpop_jkt` parameter having the value of the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key using the SHA-256 hash function, as defined in [@!RFC9449] section 10. + +Following is a non-normative example of an authentication request using the authorization code flow: + +```text +GET /authorize? +response_type=code +&dpop_jkt=dnfb1T9jil_gOhti60baHs_WD_a4D8JN9VDJXbmBmGw +&scope=openid%20profile%20email%20bound_key +&client_id=s6BhdRkqt3 +&state=af0ifjsldkj +&nonce=2a50f9ea812f9bb4c8f7 +&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1 +Host: server.example.com +``` + +If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. + +### Authentication Response - Authorization Code Flow + +If the key provided was not previously bound to the client, the OP SHOULD inform a user and obtain consent that a key binding will be done. + +On successful authentication of, and consent from the user, the OP returns an authorization `code`. + +Following is a non-normative example of a response: + +```text +HTTP/1.1 302 Found +Location: https://client.example.org/cb? + code=SplxlOBeZQQYbYS6WxSbIA + &state=af0ifjsldkj +``` + +### Token Request - Authorization Code Flow + +To obtain the ID Token, the RP authenticating component: + +1. generates `c_s256` by computing SHA256 hash of the authorization `code` encoded as `BASE64URL(SHA256(code))` +2. generates a `DPoP` header, including the `c_s256` claim in the `DPoP` header JWT. This binds the authorization `code` to the token request. + +Non-normative example of a confidential client setting `Authorization: Basic` per [@!OpenID.Core] 3.1.3.1: + +```text +POST /token HTTP/1.1 +Host: server.example.com +Content-Type: application/x-www-form-urlencoded +Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW +DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ + IkVDIiwieCI6InVrcHYzZlU2dHFRS2FVd2NkQkFRb0szSUh2SklXX185eU5kMW\ + 9SN3F2WmMiLCJ5IjoibkJCeFhyeDBOeml3Z19ldmZVTVVVZ25HS0tVZjJBVHBX\ + RzlFb2puVW9VNCJ9LCJ0eXAiOiJkcG9wK2p3dCJ9.eyJjX3MyNTYiOiJvMXVCc\ + DllU2UzRHNtU2NOMGpZcmlGZ0tLRmRLLUJMeXdDOVdScFY1R0c4IiwiaHRtIjo\ + iUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuI\ + iwiaWF0IjoxNzYxOTM3NDQ5LCJqdGkiOiJJUVM1dFlQLWJwQlB0SnNvclQ0ejd\ + nIn0.ay7H-sV7o_NE19Qfdq7oFNZ_oH-8LRw7_dgiTRQAUusLjEhgzNYR1ZU1T\ + 6IZGopiTEk55LPu_g0gKKku96d4kA +grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA +&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb +``` + +`Authorization: Basic` HTTP header is only included if a confidential client is used. + +If a DPoP header is included in the token request to the OP, and the `dpop_jkt` parameter was not included in the authentication request, the OP MUST NOT include the `cnf` claim in the ID Token. + +> This prevents an existing deployment using DPoP for access token from having key-bound ID Tokens issued accidentally. + +The OP MUST: + +- perform all verification steps as described in [@!RFC9449] section 5. +- calculate the `c_s256` from the authorization `code` just as the RP component did. +- confirm the `c_s256` in the DPoP JWT matches its calculated `c_s256` + +## Device Authorization Flow + The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`. 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request @@ -170,34 +246,7 @@ The Device Authorization Flow follows the pattern of the Authorization Code Flow +----------+ +------+ ``` -## OpenID Connect Metadata - -The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: - -- the `bound_key` scope in the `supported_scopes` -- the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] - -## Authentication Request - Authorization Code Flow - -If the RP authenticating component is running on a device that supports a web browser, it makes an authorization request per [@!OpenID.Core] 3.1. In addition to the `scope` parameter containing `openid`, and the `response_type` having the value `code`, the `scope` parameter MUST also include `bound_key`, and the request MUST include the `dpop_jkt` parameter having the value of the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key using the SHA-256 hash function, as defined in [@!RFC9449] section 10. - -Following is a non-normative example of an authentication request using the authorization code flow: - -```text -GET /authorize? -response_type=code -&dpop_jkt=dnfb1T9jil_gOhti60baHs_WD_a4D8JN9VDJXbmBmGw -&scope=openid%20profile%20email%20bound_key -&client_id=s6BhdRkqt3 -&state=af0ifjsldkj -&nonce=2a50f9ea812f9bb4c8f7 -&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1 -Host: server.example.com -``` - -If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. - -## Authentication Request - Device Authorization Flow +### Authentication Request - Device Authorization Flow If the RP authenticating component is running on a device that does not support a web browser, it makes an authorization request per [@!RFC8628] 3.1. In the request, the `scope` parameter MUST contain both `openid` and `bound_key`. The request MUST include the `dpop_jkt` parameter having the value of the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key using the SHA-256 hash function, as defined in [@!RFC9449] section 10. @@ -216,21 +265,6 @@ Host: server.example.com If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. -## Authentication Response - Authorization Code Flow - -If the key provided was not previously bound to the client, the OP SHOULD inform a user and obtain consent that a key binding will be done. - -On successful authentication of, and consent from the user, the OP returns an authorization `code`. - -Following is a non-normative example of a response: - -```text -HTTP/1.1 302 Found -Location: https://client.example.org/cb? - code=SplxlOBeZQQYbYS6WxSbIA - &state=af0ifjsldkj -``` - ## Authentication Response - Device Authorization Flow As per [@!RFC8628], the OP in response to the Authentication Request, generates and returns to the RP authenticating component the required parameters `device_code`, `user_code`, `verification_uri` and `expires_in` and may return the optional parameters `verification_uri_complete` and `interval`. @@ -247,46 +281,7 @@ Following is a non-normative example of an authentication response using the dev } ``` -## Token Request - Authorization Code Flow - -To obtain the ID Token, the RP authenticating component: - -1. generates `c_s256` by computing SHA256 hash of the authorization `code` encoded as `BASE64URL(SHA256(code))` -2. generates a `DPoP` header, including the `c_s256` claim in the `DPoP` header JWT. This binds the authorization `code` to the token request. - -Non-normative example of a confidential client setting `Authorization: Basic` per [@!OpenID.Core] 3.1.3.1: - -```text -POST /token HTTP/1.1 -Host: server.example.com -Content-Type: application/x-www-form-urlencoded -Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW -DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ - IkVDIiwieCI6InVrcHYzZlU2dHFRS2FVd2NkQkFRb0szSUh2SklXX185eU5kMW\ - 9SN3F2WmMiLCJ5IjoibkJCeFhyeDBOeml3Z19ldmZVTVVVZ25HS0tVZjJBVHBX\ - RzlFb2puVW9VNCJ9LCJ0eXAiOiJkcG9wK2p3dCJ9.eyJjX3MyNTYiOiJvMXVCc\ - DllU2UzRHNtU2NOMGpZcmlGZ0tLRmRLLUJMeXdDOVdScFY1R0c4IiwiaHRtIjo\ - iUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuI\ - iwiaWF0IjoxNzYxOTM3NDQ5LCJqdGkiOiJJUVM1dFlQLWJwQlB0SnNvclQ0ejd\ - nIn0.ay7H-sV7o_NE19Qfdq7oFNZ_oH-8LRw7_dgiTRQAUusLjEhgzNYR1ZU1T\ - 6IZGopiTEk55LPu_g0gKKku96d4kA -grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA -&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb -``` - -`Authorization: Basic` HTTP header is only included if a confidential client is used. - -If a DPoP header is included in the token request to the OP, and the `dpop_jkt` parameter was not included in the authentication request, the OP MUST NOT include the `cnf` claim in the ID Token. - -> This prevents an existing deployment using DPoP for access token from having key-bound ID Tokens issued accidentally. - -The OP MUST: - -- perform all verification steps as described in [@!RFC9449] section 5. -- calculate the `c_s256` from the authorization `code` just as the RP component did. -- confirm the `c_s256` in the DPoP JWT matches its calculated `c_s256` - -## Token Request - Device Authorization Flow +### Token Request - Device Authorization Flow As per [@!RFC8628] the RP authenticating component makes token requests to OP at regular intervals. Prior to the OP authenticating and obtaining consent from the user, the OP returns an error. From ca3345e8bdc982f2783eaba6a42b9053cf6a5c3d Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Tue, 28 Apr 2026 09:08:40 -0400 Subject: [PATCH 2/6] Rolling back some changes --- openid-connect-key-binding-1_0.md | 110 +++++++++++++++--------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index 496874e..d59dcb2 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -105,14 +105,16 @@ The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: - the `bound_key` scope in the `supported_scopes` - the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] -## Authorization Code Flow +## Protocol Profile Overview -The Authorization Code Flow works as follows: +This spec works by adding parameters and headers to the Authentication Request and Token Request and validating these headers such that the ID Token returned in the Token Response contain a `cnf` claim for a public key. -1. The RP performs OpenID Connect Authentication Request but with the addition of the `bound_key` scope and a `dpop_jkt`parameter set to the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key. -2. The OP authenticates the user and responds with the authorization `code` as usual in the Authentication Response. -3. The RP makes a Token Request to the OP `token_endpoint` with a `DPoP` header that includes the SHA-256 hash of the `code`, `c_s256`, claims. -4. The OP returns an ID Token that has a the `cnf` claim containing the RP specified proof-of-possession public key +For the Authorization Code Flow the following changes are made + +1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request +2. receiving the authorization `code` as usual in the Authentication Response +3. adding the `DPoP` header that includes the SHA-256 hash of the `code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` +4. adding the `cnf` claim containing the public key to the returned ID Token ``` +------+ +------+ @@ -131,7 +133,43 @@ The Authorization Code Flow works as follows: +------+ +------+ ``` -### Authentication Request - Authorization Code Flow +The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`. + +1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request +2. receiving the `device_code` as usual in the Device Authentication Response +3. user opens browser to Verification URI +4. user authentications and consents +5. adding the `DPoP` header that includes the SHA-256 hash of the `device code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` +6. adding the `cnf` claim containing the public key to the returned ID Token + +``` ++----------+ +------+ +| |-- Authentication Request ----->| | +| RP | (1) bound_key & dpop_jkt | OP | +| (device | | | +| client) |<-- Authentication Response ----| | +| | (2) device_code, user code | | +| | & Verification URI | | +| | | | +| | [polling] | | +| |-- Token Request -------------->| | +| | (5) DPoP header w/ c_s256 | | +| | c_s256 = SHA256(device_code) | | +| | | | +| |<-- Token Response -------------| | ++----------+ +------+ +``` + +## OpenID Connect Metadata + +The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: + +- the `bound_key` scope in the `supported_scopes` +- the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] + +# Authorization Code Flow + +## Authentication Request If the RP authenticating component is running on a device that supports a web browser, it makes an authorization request per [@!OpenID.Core] 3.1. In addition to the `scope` parameter containing `openid`, and the `response_type` having the value `code`, the `scope` parameter MUST also include `bound_key`, and the request MUST include the `dpop_jkt` parameter having the value of the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key using the SHA-256 hash function, as defined in [@!RFC9449] section 10. @@ -151,7 +189,7 @@ Host: server.example.com If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. -### Authentication Response - Authorization Code Flow +## Authentication Response If the key provided was not previously bound to the client, the OP SHOULD inform a user and obtain consent that a key binding will be done. @@ -166,7 +204,7 @@ Location: https://client.example.org/cb? &state=af0ifjsldkj ``` -### Token Request - Authorization Code Flow +## Token Request To obtain the ID Token, the RP authenticating component: @@ -205,48 +243,9 @@ The OP MUST: - calculate the `c_s256` from the authorization `code` just as the RP component did. - confirm the `c_s256` in the DPoP JWT matches its calculated `c_s256` -## Device Authorization Flow - -The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`. - -1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request -2. receiving the `device_code` as usual in the Device Authentication Response -3. user opens browser to Verification URI -4. user authentications and consents -5. adding the `DPoP` header that includes the SHA-256 hash of the `device code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` -6. adding the `cnf` claim containing the public key to the returned ID Token - -``` -+----------+ +------+ -| |-- Authentication Request ----->| | -| RP | (1) bound_key & dpop_jkt | OP | -| (device | | | -| client) |<-- Authentication Response ----| | -| | (2) device_code, user code | | -| | & Verification URI | | -| | | | -| | [polling] | | -| |-- Token Request -------------->| | -| | (5) DPoP header w/ c_s256 | | -| | c_s256 = SHA256(device_code) | | -| | | | -| |<-- Token Response -------------| | -| | (6) cnf claim containing | | -| | the public key in ID Token | | -+----------+ | | - v | | - : | | - (3) user code & verification URI | | - : | | - v | | -+----------+ | | -| End user | | | -| at |<-- (4). End user consents ---->| | -| browser | & authenticates | | -+----------+ +------+ -``` +# Device Authorization Flow -### Authentication Request - Device Authorization Flow +## Authentication Request If the RP authenticating component is running on a device that does not support a web browser, it makes an authorization request per [@!RFC8628] 3.1. In the request, the `scope` parameter MUST contain both `openid` and `bound_key`. The request MUST include the `dpop_jkt` parameter having the value of the JWK Thumbprint [@!RFC7638] of the proof-of-possession public key using the SHA-256 hash function, as defined in [@!RFC9449] section 10. @@ -265,7 +264,7 @@ Host: server.example.com If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. -## Authentication Response - Device Authorization Flow +## Authentication Response As per [@!RFC8628], the OP in response to the Authentication Request, generates and returns to the RP authenticating component the required parameters `device_code`, `user_code`, `verification_uri` and `expires_in` and may return the optional parameters `verification_uri_complete` and `interval`. @@ -281,7 +280,7 @@ Following is a non-normative example of an authentication response using the dev } ``` -### Token Request - Device Authorization Flow +## Token Request As per [@!RFC8628] the RP authenticating component makes token requests to OP at regular intervals. Prior to the OP authenticating and obtaining consent from the user, the OP returns an error. @@ -389,7 +388,7 @@ If an ID Token is returned as a result of a Refresh Request, an additional requi If a new Refresh Token is returned as a result of a Refresh Request, the newly issued Refresh Token MUST continue to be bound to the same public key as the original Refresh Token. -## ID Token Proof of Possession +# ID Token Proof of Possession The mechanism for how an RP authenticating component proves to an RP consuming component that it possesses the private keys associated with the `cnf` claim in the ID Token is out of scope of this document. @@ -439,12 +438,11 @@ Subtype name: dpop+id_token # Acknowledgements -The authors would like to thank early feedback provided by Filip Skokan, Frederik Krogsdal Jacobsen, George Fletcher, Jacob Ideskog, Karl McGuinness, and Kosuke Koiwai. - +The authors would like to thank early feedback provided by Filip Skokan, Frederik Krogsdal Jacobsen, George Fletcher, Jacob Ideskog, Jonas Primbs, Karl McGuinness, and Kosuke Koiwai. # Notices -Copyright (c) 2025 The OpenID Foundation. +Copyright (c) 2026 The OpenID Foundation. The OpenID Foundation (OIDF) grants to any Contributor, developer, implementer, or other interested party a non-exclusive, royalty free, From 00ca980790ecff34f8cc7e86d28d9ab03d683279 Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Tue, 28 Apr 2026 09:56:54 -0400 Subject: [PATCH 3/6] Fixes typos, makes typ consistent --- openid-connect-key-binding-1_0.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index d59dcb2..f69cfeb 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -64,11 +64,11 @@ This specification defines how to bind a public key to an OpenID Connect ID Toke # Introduction -OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication to and obtain identity claims from an OpenID Connect Provider (OP). When authenticating with OpenID Connect an RP initiates the protocol by making an authentication request to the OP. The OP after authenticates the identity of the user and sends the RP an ID Token signed by the OP and containing claims about the user. +OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication to and obtain identity claims from an OpenID Connect Provider (OP). When authenticating with OpenID Connect an RP initiates the protocol by making an authentication request to the OP. The OP authenticates the identity of the user and sends the RP an ID Token signed by the OP and containing claims about the user. It is common for an RP to be composed of multiple components such as a RP authenticating component that obtains the ID Token from the OP and an RP consuming component which checks the ID Token presented to it by the authenticating component. When the RP authenticating component wants to prove to an RP consuming component that it has authenticated a user, it may present the ID Token as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks. For instance, if an attacker obtains the ID Token, they can impersonate the authenticated user. -By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that a user has been authenticated, but that the RP authenticating component itself was the original recipient of that authentication. This provides stronger security guarantees, prevent token theft and replay attacks, by transforming the ID Token from a bearer token into a proof-of-possession token. +By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that a user has been authenticated, but that the RP authenticating component itself was the original recipient of that authentication. This provides stronger security guarantees, preventing token theft and replay attacks, by transforming the ID Token from a bearer token into a proof-of-possession token. Use cases include: a mobile app that has received an ID Token exchanging the ID Token with a proof-of-possession with a first party authorization service for an access token; an instance of a peer to peer application such as video conferencing where one instance of the application sends the ID Token with a proof of possession to a second instance to prove which user is operating the first instance. @@ -107,7 +107,7 @@ The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: ## Protocol Profile Overview -This spec works by adding parameters and headers to the Authentication Request and Token Request and validating these headers such that the ID Token returned in the Token Response contain a `cnf` claim for a public key. +This spec works by adding parameters and headers to the Authentication Request and Token Request and then validating these fields such that the ID Token returned in the Token Response contains a `cnf` claim for a public key. For the Authorization Code Flow the following changes are made @@ -138,7 +138,7 @@ The Device Authorization Flow follows the pattern of the Authorization Code Flow 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request 2. receiving the `device_code` as usual in the Device Authentication Response 3. user opens browser to Verification URI -4. user authentications and consents +4. user authenticates and consents 5. adding the `DPoP` header that includes the SHA-256 hash of the `device code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` 6. adding the `cnf` claim containing the public key to the returned ID Token @@ -259,7 +259,6 @@ dpop_jkt=dnfb1T9jil_gOhti60baHs_WD_a4D8JN9VDJXbmBmGw &scope=openid%20profile%20email%20bound_key &client_id=s6BhdRkqt3 &nonce=KDOmGsiiMaiq-ZhBE-RmPgCsrH-bs-wqbqD2FsRWf7g -Host: server.example.com ``` If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!OpenID.Core] 3.1.2.1. @@ -325,7 +324,7 @@ The OP MUST: ## Token Response -If the token request was successful, the OP MUST return an ID Token containing the `cnf` claim as defined in [@!RFC7800] set to the jwk of the user's public key and with `typ` set to `id_token+cnf` in the ID Token's protected header. +If the token request was successful, the OP MUST return an ID Token containing the `cnf` claim as defined in [@!RFC7800] set to the jwk of the user's public key and with `typ` set to `dpop+id_token` in the ID Token's protected header. Non-normative example of the ID Token payload: @@ -412,7 +411,7 @@ An RP consuming component MUST NOT trust an ID Token with a `cnf` claim without ## ID Token Reverification -In addition to verifying the signature created by the RP authenticating component to prove possession of the private key associated with the `cnf` claim in the ID Token, an RP consuming component MUST independently verify the signature and validity of the ID Token and that the `aud` claim in the payload is the correct value, and that the `typ` claim in the protected header is `id_token+cnf`. +In addition to verifying the signature created by the RP authenticating component to prove possession of the private key associated with the `cnf` claim in the ID Token, an RP consuming component MUST independently verify the signature and validity of the ID Token and that the `aud` claim in the payload is the correct value, and that the `typ` claim in the protected header is `dpop+id_token`. ## Use as Access Token From 8737032710979efb2687e77fe3ca5ea35ae1733b Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Wed, 29 Apr 2026 16:23:17 -0400 Subject: [PATCH 4/6] Adds fleshed out wording and context to descriptions --- openid-connect-key-binding-1_0.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index f69cfeb..9585ff7 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -109,7 +109,11 @@ The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: This spec works by adding parameters and headers to the Authentication Request and Token Request and then validating these fields such that the ID Token returned in the Token Response contains a `cnf` claim for a public key. -For the Authorization Code Flow the following changes are made +This specification extends the OpenID Connect Authentication Request addition of the parameter `dpop_jkt` to the Authentication Request, and a `DPoP` header to the Token Request and Refresh Request. +The RP signals to the OP it is requesting a key bound ID Token by including the scope `bound_key` in the Authentication Request. If the OP chooses to key bound ID Token it validates the `dpop_jkt` parameter and `DPoP` and returns an ID Token in the Token Response that includes a `cnf` claim for the bound public key. +This specification does not add new messages, requests or responses, preserving the current OpenID Connect flows and interactions. + +For the Authorization Code Flow the following changes are made: 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request 2. receiving the authorization `code` as usual in the Authentication Response @@ -133,7 +137,7 @@ For the Authorization Code Flow the following changes are made +------+ +------+ ``` -The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`. +The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`, making the following changes: 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request 2. receiving the `device_code` as usual in the Device Authentication Response @@ -351,7 +355,7 @@ Non-normative example of the ID Token payload: The OP MAY return a Refresh Token. If a Refresh Token is returned, it MUST be bound to the public key of the DPoP proof used in the Token Request i.e. the same public key bound to the ID Token. -## Refresh Request +# Refresh Request If a Refresh Token was returned in the Token Response, the RP may use the Refresh Token to make Refresh Requests to the OP's Token Endpoint and receive a refreshed ID Token ([@!OpenID.Core] 12). This Refresh Token MUST be bound to the same public key as the ID Token and the OP MUST validate a DPoP proof ([@!RFC9449] 5) for this public key on each refresh request. @@ -380,6 +384,7 @@ grant_type=refresh_token&refresh_token=8xLOxBtZp8 The OP MUST validate the Refresh Token and MUST validate the `DPoP` header presented. The OP MUST reject the `DPoP` header if it is not signed with the public key that was bound to the presented Refresh Token in the initial Token Request. +Unlike the Token Request, no `c_s256` claim is required in the the `DPoP`header for the Refresh Request. If an ID Token is returned as a result of a Refresh Request, an additional requirement applies: From 69778652a6f59d75dbe81a9d71aa101c40ce8ae2 Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Wed, 29 Apr 2026 20:07:18 -0400 Subject: [PATCH 5/6] Fixes typos, cleans up notes and language --- openid-connect-key-binding-1_0.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index 9585ff7..775f61b 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -109,7 +109,7 @@ The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: This spec works by adding parameters and headers to the Authentication Request and Token Request and then validating these fields such that the ID Token returned in the Token Response contains a `cnf` claim for a public key. -This specification extends the OpenID Connect Authentication Request addition of the parameter `dpop_jkt` to the Authentication Request, and a `DPoP` header to the Token Request and Refresh Request. +This specification extends the OpenID Connect with the addition of the parameter `dpop_jkt` to the Authentication Request, and a `DPoP` header to the Token Request and Refresh Request. The RP signals to the OP it is requesting a key bound ID Token by including the scope `bound_key` in the Authentication Request. If the OP chooses to key bound ID Token it validates the `dpop_jkt` parameter and `DPoP` and returns an ID Token in the Token Response that includes a `cnf` claim for the bound public key. This specification does not add new messages, requests or responses, preserving the current OpenID Connect flows and interactions. @@ -396,8 +396,6 @@ If a new Refresh Token is returned as a result of a Refresh Request, the newly i The mechanism for how an RP authenticating component proves to an RP consuming component that it possesses the private keys associated with the `cnf` claim in the ID Token is out of scope of this document. -> If the WG wants to, we can also profile how to use KB to bind a proof of possession to an ID Token for presentation when a proof of possession is not present. - # Privacy Considerations An RP authenticating component SHOULD only share an ID Token with a consuming component when such sharing is consistent with the original purpose for which the PII was collected and the scope of consent obtained from the user. From cddc4062cefac02acbf8db8ce97bf6b164fe7a53 Mon Sep 17 00:00:00 2001 From: Ethan Heilman Date: Thu, 30 Apr 2026 18:45:54 -0400 Subject: [PATCH 6/6] Fix typos, clarify details, PR should be complete now --- openid-connect-key-binding-1_0.md | 62 +++++++++++++++---------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/openid-connect-key-binding-1_0.md b/openid-connect-key-binding-1_0.md index 775f61b..186c73b 100644 --- a/openid-connect-key-binding-1_0.md +++ b/openid-connect-key-binding-1_0.md @@ -64,13 +64,13 @@ This specification defines how to bind a public key to an OpenID Connect ID Toke # Introduction -OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication to and obtain identity claims from an OpenID Connect Provider (OP). When authenticating with OpenID Connect an RP initiates the protocol by making an authentication request to the OP. The OP authenticates the identity of the user and sends the RP an ID Token signed by the OP and containing claims about the user. +OpenID Connect is a protocol that enables a Relying Party (RP) to delegate authentication to and obtain identity claims from an OpenID Connect Provider (OP). When authenticating with OpenID Connect, an RP initiates the protocol by making an authentication request to the OP. The OP authenticates the identity of the user and sends the RP an ID Token signed by the OP and containing claims about the user. -It is common for an RP to be composed of multiple components such as a RP authenticating component that obtains the ID Token from the OP and an RP consuming component which checks the ID Token presented to it by the authenticating component. When the RP authenticating component wants to prove to an RP consuming component that it has authenticated a user, it may present the ID Token as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks. For instance, if an attacker obtains the ID Token, they can impersonate the authenticated user. +It is common for an RP to be composed of multiple components such as an RP authenticating component that obtains the ID Token from the OP and an RP consuming component which checks the ID Token presented to it by the authenticating component. When the RP authenticating component wants to prove to an RP consuming component that it has authenticated a user, it may present the ID Token as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks. For instance, if an attacker obtains the ID Token, they can impersonate the authenticated user. By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that a user has been authenticated, but that the RP authenticating component itself was the original recipient of that authentication. This provides stronger security guarantees, preventing token theft and replay attacks, by transforming the ID Token from a bearer token into a proof-of-possession token. -Use cases include: a mobile app that has received an ID Token exchanging the ID Token with a proof-of-possession with a first party authorization service for an access token; an instance of a peer to peer application such as video conferencing where one instance of the application sends the ID Token with a proof of possession to a second instance to prove which user is operating the first instance. +Use cases for this include: a mobile app that has received an ID Token exchanging the ID Token with a proof of possession to a first party authorization service for an access token; an instance of a peer-to-peer application such as video conferencing where one instance of the application sends the ID Token with a proof of possession to a second instance to prove which user is operating the first instance. This specification profiles OpenID Connect 1.0 [@!OpenID.Core], RFC8628 - OAuth 2.0 Device Authorization Grant [@!RFC8628], and RFC9449 - OAuth 2.0 Demonstrating Proof of Possession (DPoP) [@!RFC9449] to enable cryptographically bound ID Tokens that resist theft and replay attacks while maintaining compatibility with existing OpenID Connect infrastructure. @@ -102,22 +102,24 @@ The parameters **dpop_jkt** and **DPoP** as defined in [@!RFC9449] The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: -- the `bound_key` scope in the `supported_scopes` +- the `bound_key` scope in the `scopes_supported` - the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] ## Protocol Profile Overview -This spec works by adding parameters and headers to the Authentication Request and Token Request and then validating these fields such that the ID Token returned in the Token Response contains a `cnf` claim for a public key. +This specification works by adding parameters and headers to the Authentication Request and Token Request and then validating these fields such that the ID Token returned in the Token Response contains a `cnf` claim for a public key. +The RP signals to the OP it is requesting a key-bound ID Token by including the scope `bound_key` in the Authentication Request. -This specification extends the OpenID Connect with the addition of the parameter `dpop_jkt` to the Authentication Request, and a `DPoP` header to the Token Request and Refresh Request. -The RP signals to the OP it is requesting a key bound ID Token by including the scope `bound_key` in the Authentication Request. If the OP chooses to key bound ID Token it validates the `dpop_jkt` parameter and `DPoP` and returns an ID Token in the Token Response that includes a `cnf` claim for the bound public key. -This specification does not add new messages, requests or responses, preserving the current OpenID Connect flows and interactions. +This specification extends OpenID Connect with the addition of a parameter, `dpop_jkt`, to the Authentication Request, and the addition of a `DPoP` header to the Token Request and Refresh Request. +If the OP chooses to issue a key-bound ID Token it validates the `dpop_jkt` parameter and `DPoP` header and returns an ID Token in the Token Response which includes a `cnf` claim for the public key. +This specification does not add new messages, requests or responses. +It preserves the current OpenID Connect flows and interactions. For the Authorization Code Flow the following changes are made: 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request 2. receiving the authorization `code` as usual in the Authentication Response -3. adding the `DPoP` header that includes the SHA-256 hash of the `code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` +3. adding the `DPoP` header that includes the SHA-256 hash of the `code` as the claim `c_s256` in the Token Request to the OP `token_endpoint` 4. adding the `cnf` claim containing the public key to the returned ID Token ``` @@ -137,14 +139,12 @@ For the Authorization Code Flow the following changes are made: +------+ +------+ ``` -The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets `c_s256` to SHA-256 of the `device_code` in place of the authorization `code`, making the following changes: +The Device Authorization Flow follows the pattern of the Authorization Code Flow but sets the claim `c_s256` to the SHA-256 of the `device_code` in place of the authorization `code`, making the following changes: 1. adding the `bound_key` scope and `dpop_jkt` parameter to the OpenID Connect Authentication Request 2. receiving the `device_code` as usual in the Device Authentication Response -3. user opens browser to Verification URI -4. user authenticates and consents -5. adding the `DPoP` header that includes the SHA-256 hash of the `device code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` -6. adding the `cnf` claim containing the public key to the returned ID Token +3. adding the `DPoP` header that includes the SHA-256 hash of the `device_code`, `c_s256`, as a claim in the Token Request to the OP `token_endpoint` +4. adding the `cnf` claim containing the public key to the returned ID Token ``` +----------+ +------+ @@ -152,25 +152,20 @@ The Device Authorization Flow follows the pattern of the Authorization Code Flow | RP | (1) bound_key & dpop_jkt | OP | | (device | | | | client) |<-- Authentication Response ----| | -| | (2) device_code, user code | | +| | (2) device_code, user_code | | | | & Verification URI | | | | | | | | [polling] | | | |-- Token Request -------------->| | -| | (5) DPoP header w/ c_s256 | | +| | (3) DPoP header w/ c_s256 | | | | c_s256 = SHA256(device_code) | | | | | | | |<-- Token Response -------------| | +| | (4) cnf claim containing | | +| | the public key in ID Token | | +----------+ +------+ ``` -## OpenID Connect Metadata - -The OP's OpenID Connect Metadata Document [@!OpenID.Discovery] SHOULD include: - -- the `bound_key` scope in the `supported_scopes` -- the `dpop_signing_alg_values_supported` property containing a list of supported algorithms as defined in [@?IANA.JOSE.ALGS] - # Authorization Code Flow ## Authentication Request @@ -195,7 +190,7 @@ If the OP does not support the `bound_key` scope, it SHOULD ignore it per [@!Ope ## Authentication Response -If the key provided was not previously bound to the client, the OP SHOULD inform a user and obtain consent that a key binding will be done. +If the key provided was not previously bound to the client, the OP SHOULD inform the user and obtain consent that a key binding will be done. On successful authentication of, and consent from the user, the OP returns an authorization `code`. @@ -212,7 +207,7 @@ Location: https://client.example.org/cb? To obtain the ID Token, the RP authenticating component: -1. generates `c_s256` by computing SHA256 hash of the authorization `code` encoded as `BASE64URL(SHA256(code))` +1. generates `c_s256` by computing SHA256 hash of the authorization `code` encoded as `BASE64URL(SHA256(ASCII(code)))` 2. generates a `DPoP` header, including the `c_s256` claim in the `DPoP` header JWT. This binds the authorization `code` to the token request. Non-normative example of a confidential client setting `Authorization: Basic` per [@!OpenID.Core] 3.1.3.1: @@ -231,6 +226,7 @@ DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ iwiaWF0IjoxNzYxOTM3NDQ5LCJqdGkiOiJJUVM1dFlQLWJwQlB0SnNvclQ0ejd\ nIn0.ay7H-sV7o_NE19Qfdq7oFNZ_oH-8LRw7_dgiTRQAUusLjEhgzNYR1ZU1T\ 6IZGopiTEk55LPu_g0gKKku96d4kA + grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb ``` @@ -292,7 +288,7 @@ Once the OP has authenticated and obtained consent from the user, the OP respond In addition to the parameters required by [@!RFC8628] the token request to the OP must contain a DPoP header. The RP authenticating component computes this DPoP header as follows: -1. generates `c_s256` by computing SHA-256 hash of the authorization `device_code` encoded as `BASE64URL(SHA256(device_code))` +1. generates `c_s256` by computing SHA-256 hash of the authorization `device_code` encoded as `BASE64URL(SHA256(ASCII(device_code)))` 2. generates a `DPoP` header, including the `c_s256` claim in the `DPoP` header JWT. This binds the authorization `device_code` to the token request. Non-normative example of a token request: @@ -311,6 +307,7 @@ DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ iwiaWF0IjoxNzYxOTM3NDQ5LCJqdGkiOiJJUVM1dFlQLWJwQlB0SnNvclQ0ejd\ nIn0.9t65IuqqvabsJp4v9CpY_pj7ad97KCdR9LXXF-pFvUokP_h2OZ2KqlM10\ O-l-vebFVHk0qbm1pcw3MWH_VhO7A + grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code &device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS &client_id=app_fzr7iWr50CWQkGDrLCZBYQc4_2Ak @@ -326,7 +323,7 @@ The OP MUST: - calculate the `c_s256` from the authorization `device_code` just as the RP component did. - confirm the `c_s256` in the DPoP JWT matches its calculated `c_s256` -## Token Response +# Token Response If the token request was successful, the OP MUST return an ID Token containing the `cnf` claim as defined in [@!RFC7800] set to the jwk of the user's public key and with `typ` set to `dpop+id_token` in the ID Token's protected header. @@ -379,12 +376,13 @@ DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ 3NjE5Mzc4MjMsImp0aSI6ImJHOXpaV1psYm1ObFkyaHZiM05sY20ifQ.NVmGXw\ opPNYiN7CpITgR0Fl1PYFFgIAbxPxs8N1llDPoQmR60il35b-Zez71eMkdM9gd\ oqJkee3oKrimdrsCfA + grant_type=refresh_token&refresh_token=8xLOxBtZp8 ``` The OP MUST validate the Refresh Token and MUST validate the `DPoP` header presented. The OP MUST reject the `DPoP` header if it is not signed with the public key that was bound to the presented Refresh Token in the initial Token Request. -Unlike the Token Request, no `c_s256` claim is required in the the `DPoP`header for the Refresh Request. +Unlike the Token Request, no `c_s256` claim is required in the `DPoP`header for the Refresh Request. If an ID Token is returned as a result of a Refresh Request, an additional requirement applies: @@ -398,7 +396,7 @@ The mechanism for how an RP authenticating component proves to an RP consuming c # Privacy Considerations -An RP authenticating component SHOULD only share an ID Token with a consuming component when such sharing is consistent with the original purpose for which the PII was collected and the scope of consent obtained from the user. +An RP authenticating component SHOULD only share an ID Token with a consuming component when such sharing is consistent with the original purpose for which the identity data was collected and the scope of consent obtained from the user. # Security Considerations @@ -406,7 +404,7 @@ An RP authenticating component SHOULD only share an ID Token with a consuming co A public key substitution attack is a type of Unknown Key Share (UKS) attack in which an adversary binds the adversary identity to another party's key. -To protect against such attacks, the `DPoP` header JWT sent in the Token Request MUST include the `c_s256` claim which contains the SHA-256 of the authorization `code`. This prevents replaying of the `DPoP` header JWTs between authentication sessions as each `DPoP` header JWT in a Token Request is now strictly bound to the specific authentication `code` for that session. +To protect against such attacks, the `DPoP` header JWT sent in the Token Request MUST include the `c_s256` claim which contains the SHA-256 of the authorization `code`, or in the case of the Device Authorization Flow the SHA-256 of the `device_code`. This prevents replaying of the `DPoP` header JWTs between authentication sessions as each `DPoP` header JWT in a Token Request is now strictly bound to that session. ## Require Proof of Possession @@ -414,7 +412,7 @@ An RP consuming component MUST NOT trust an ID Token with a `cnf` claim without ## ID Token Reverification -In addition to verifying the signature created by the RP authenticating component to prove possession of the private key associated with the `cnf` claim in the ID Token, an RP consuming component MUST independently verify the signature and validity of the ID Token and that the `aud` claim in the payload is the correct value, and that the `typ` claim in the protected header is `dpop+id_token`. +In addition to verifying the signature created by the RP authenticating component to prove possession of the private key associated with the `cnf` claim in the ID Token, an RP consuming component MUST independently verify the signature and validity of the ID Token, that the `aud` claim in the payload is the correct value, and that the `typ` claim in the protected header is `dpop+id_token`. ## Use as Access Token @@ -426,7 +424,7 @@ To prevent token confusion attacks, the RP authenticating component SHOULD bind ## Using cnf as a User Claim -The `cnf` claim in the ID Token MUST be verified together with proof of possession and MUST NOT be treated as proof on its own. A proof of possession is REQUIRED to establish that a party controls the key identified by `cnf`. The `cnf` claim SHOULD only be used to bind a signed object with the other claims in the ID Token. +The `cnf` claim in the ID Token MUST be verified together with a proof of possession and MUST NOT be treated as proof on its own. A proof of possession is REQUIRED to establish that a party controls the key identified by `cnf`. The `cnf` claim SHOULD only be used to bind a signed object with the other claims in the ID Token. # IANA Considerations