| openid-connect-key-binding | May 2026 | |
| Hardt & Heilman | Standards Track | [Page] |
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).¶
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 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 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.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].¶
In the .txt version of this specification, values are quoted to indicate that they are to be taken literally. When using these values in protocol messages, the quotes MUST NOT be used as part of the value. In the HTML version of this specification, values to be taken literally are indicated by the use of this fixed-width font.¶
This specification uses the following terms:¶
OP: The OpenID Provider as defined in [OpenID.Core].¶
RP: The Relying Party as defined in [OpenID.Core].¶
The OP's OpenID Connect Metadata Document [OpenID.Discovery] SHOULD include:¶
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]¶
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 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:¶
adding the bound_key scope and dpop_jkt parameter to the OpenID Connect Authentication Request¶
receiving the authorization code as usual in the Authentication Response¶
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¶
adding the cnf claim containing the public key to the returned ID Token¶
+------+ +------+ | |-- Authentication Request --->| | | RP | (1) bound_key & dpop_jkt | OP | | | | | | |<-- Authentication Response --| | | | (2) authorization code | | | | | | | |-- Token Request ------------>| | | | (3) DPoP header w/ c_s256 | | | | | | | |<-- Token Response -----------| | | | (4) cnf claim containing | | | | the public key in ID Token | | +------+ +------+¶
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:¶
adding the bound_key scope and dpop_jkt parameter to the OpenID Connect Authentication Request¶
receiving the device_code as usual in the Device Authentication Response¶
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¶
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 -------------->| | | | (3) DPoP header w/ c_s256 | | | | c_s256 = SHA256(device_code) | | | | | | | |<-- Token Response -------------| | | | (4) cnf claim containing | | | | the public key in ID Token | | +----------+ +------+¶
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:¶
{
"iss": "https://server.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970,
"cnf":
{
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "ukpv3fU6tqQKaUwcdBAQoK3IHvJIW__9yNd1oR7qvZc",
"y": "nBBxXrx0Nziwg_evfUMUUgnGKKUf2ATpWG9EojnUoU4"
}
}
}
¶
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.¶
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.¶
To refresh the ID Token, the RP authenticating component:¶
generates a DPoP header¶
makes a POST request to the OP's Token Endpoint with the DPoP header and the Refresh Token as a parameter.¶
Non-normative example:¶
POST /token HTTP/1.1 Host: server.example.com Content-Type: application/x-www-form-urlencoded DPoP: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6\ IkVDIiwieCI6InVrcHYzZlU2dHFRS2FVd2NkQkFRb0szSUh2SklXX185eU5kMW\ 9SN3F2WmMiLCJ5IjoibkJCeFhyeDBOeml3Z19ldmZVTVVVZ25HS0tVZjJBVHBX\ RzlFb2puVW9VNCJ9LCJ0eXAiOiJkcG9wK2p3dCJ9.eyJodG0iOiJQT1NUIiwia\ HR1IjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20vdG9rZW4iLCJpYXQiOjE\ 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 DPoPheader for the Refresh Request.¶
If an ID Token is returned as a result of a Refresh Request, an additional requirement applies:¶
its cnf claim MUST be the same as in the ID Token issued when the original authentication occurred.¶
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.¶
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.¶
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.¶
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, 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.¶
An RP consuming component MUST NOT trust an ID Token with a cnf claim without a corresponding proof of possession from the RP authenticating component.¶
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.¶
The ID Token MUST NOT be used as an access token to access resources. The RP MAY exchange the ID Token with a proof of possession for an access token that can then be used to access resources.¶
To prevent token confusion attacks, the RP authenticating component SHOULD bind a unique key pair to its ID Tokens, and not use it for other purposes.¶
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.¶
The following entry should be added to the "Media Types" registry for the new JWT type:¶
Type name: application¶
Subtype name: dpop+id_token¶
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.¶
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, worldwide copyright license to reproduce, prepare derivative works from, distribute, perform and display, this Implementers Draft, Final Specification, or Final Specification Incorporating Errata Corrections solely for the purposes of (i) developing specifications, and (ii) implementing Implementers Drafts, Final Specifications, and Final Specification Incorporating Errata Corrections based on such documents, provided that attribution be made to the OIDF as the source of the material, but that such attribution does not indicate an endorsement by the OIDF.¶
The technology described in this specification was made available from contributions from various sources, including members of the OpenID Foundation and others. Although the OpenID Foundation has taken steps to help ensure that the technology is available for distribution, it takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this specification or the extent to which any license under such rights might or might not be available; neither does it represent that it has made any independent effort to identify any such rights. The OpenID Foundation and the contributors to this specification make no (and hereby expressly disclaim any) warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to this specification, and the entire risk as to implementing this specification is assumed by the implementer. The OpenID Intellectual Property Rights policy (found at openid.net) requires contributors to offer a patent promise not to assert certain patent claims against other contributors and against implementers. OpenID invites any interested party to bring to its attention any copyrights, patents, patent applications, or other proprietary rights that may cover technology that may be required to practice this specification.¶
[[ To be removed from the final specification ]]¶
-00¶
initial draft¶