| openid-connect-key-binding | June 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 (OIDC) enables a Relying Party (RP) to obtain End-User authentication and identity claims from an OpenID Provider (OP) in the form of an ID Token. An RP initiates the protocol by making an authentication request to the OP. The OP authenticates the End-User and returns an ID Token, signed by the OP, containing claims about the End-User.¶
An RP is often composed of multiple components, such as an RP authenticating component that obtains the ID Token from the OP and an RP consuming component that checks the ID Token presented to it by the authenticating component. To prove it has authenticated an End-User, the authenticating component may present the ID Token to the consuming component as a bearer token. However, bearer tokens are vulnerable to theft and replay attacks: an attacker who obtains the ID Token can impersonate the authenticated End-User.¶
By binding a cryptographic key to the ID Token, the RP authenticating component can prove to RP consuming components not only that an End-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 exchanges an ID Token, along with a proof of possession, at a first-party authorization service for an access token;¶
a Relying Party composed of multiple components, where an authenticating component proves to a consuming component that it is the party the OP authenticated; and¶
a peer-to-peer application, such as video conferencing or messaging, where one instance proves to another which End-User is operating it.¶
The Use Cases appendix describes how key binding is applied in each of these scenarios.¶
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].¶
End-User: The End-User 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 End-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. The typ of the DPoP header JWT MUST be dpop+jwt.¶
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 End-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¶
This appendix is non-normative. It describes how the mechanisms defined in this specification are applied in representative scenarios. The mechanism by which an RP authenticating component proves possession of the private key to an RP consuming component is out of scope of this specification (see the ID Token Proof of Possession section); each use case below notes how that proof is typically realized.¶
A first-party application, such as a mobile app, obtains a key-bound ID Token and exchanges it, together with a proof of possession, at a first-party authorization service for an access token.¶
Because the ID Token carries a cnf claim, the authorization service can confirm that the party requesting the access token is the same party the OP authenticated, rather than a bearer that obtained the ID Token in transit. Without key binding, an intercepted ID Token could be replayed to obtain an access token.¶
The proof of possession is a DPoP proof computed over the exchange request. The authorization service verifies it against the cnf claim of the ID Token before issuing the access token.¶
A Relying Party is often composed of multiple components, for example a frontend that authenticates the End-User and one or more backends that act on the End-User's behalf. The authenticating component obtains the ID Token and presents it to a consuming component to prove which End-User the OP authenticated.¶
When the ID Token is key-bound, the consuming component requires a proof of possession alongside the ID Token. An attacker who captures the ID Token in transit between components cannot use it, because the attacker cannot produce the proof of possession.¶
The authenticating component proves possession on each request to a consuming component, for example with a DPoP proof over that request, and the consuming component verifies the proof against the cnf claim before trusting the ID Token.¶
In a peer-to-peer application, such as video conferencing or messaging, one instance proves to another which End-User is operating it. The instances are typically operated by different End-Users and communicate without a shared backend.¶
Consider Alice authenticating to Bob over WebRTC. With a bearer ID Token, an attacker who relays Alice's ID Token to Bob could impersonate Alice. With a key-bound ID Token, Alice signs a value that ties her authenticated identity to the connection, such as the DTLS certificate fingerprint of her media channel, using the key in the cnf claim. Bob verifies that signature against the cnf claim and is assured both that the OP authenticated Alice and that she controls the channel he is connected to.¶
The step that is not obvious to an implementer is binding the OIDC identity to the application's own identity or channel: the value signed under the cnf key must be something the consuming instance can independently associate with the session, such as a WebRTC certificate fingerprint, a messaging device key, or a per-message signature. How that value is chosen and verified is application-specific and out of scope of this specification.¶
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 ]]¶
-01¶
Added Use Cases appendix, editorial improvements, clarified DPoP proof typ must be dpop+jwt¶
-00¶
initial draft¶