Skip to main content

4. DPoP Proof JWTs

DPoP introduces the concept of a DPoP proof, which is a JWT created by the client and sent with an HTTP request using the DPoP header field. Each HTTP request requires a unique DPoP proof.

A valid DPoP proof demonstrates to the server that the client holds the private key that was used to sign the DPoP proof JWT. This enables authorization servers to bind issued tokens to the corresponding public key (as described in Section 5) and enables resource servers to verify the key-binding of tokens that it receives (see Section 7.1), which prevents said tokens from being used by any entity that does not have access to the private key.

The DPoP proof demonstrates possession of a key and, by itself, is not an authentication or access control mechanism. When presented in conjunction with a key-bound access token as described in Section 7.1, the DPoP proof provides additional assurance about the legitimacy of the client to present the access token. However, a valid DPoP proof JWT is not sufficient alone to make access control decisions.

4.1. The DPoP HTTP Header

A DPoP proof is included in an HTTP request using the following request header field.

DPoP: A JWT that adheres to the structure and syntax of Section 4.2.

Figure 2 shows an example DPoP HTTP header field. The example uses "" line wrapping per [RFC8792].

DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\
QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\
oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\
WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\
4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg

Figure 2: Example DPoP Header

Note that per [RFC9110], header field names are case insensitive; thus, DPoP, DPOP, dpop, etc., are all valid and equivalent header field names. However, case is significant in the header field value.

The DPoP HTTP header field value uses the token68 syntax defined in Section 11.2 of [RFC9110] and is repeated below in Figure 3 for ease of reference.

DPoP       = token68
token68 = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="

Figure 3: DPoP Header Field ABNF

4.2. DPoP Proof JWT Syntax

A DPoP proof is a JWT [RFC7519] that is signed (using JSON Web Signature (JWS) [RFC7515]) with a private key chosen by the client (see below). The JOSE Header of a DPoP JWT MUST contain at least the following parameters:

typ: A field with the value dpop+jwt, which explicitly types the DPoP proof JWT as recommended in Section 3.11 of [RFC8725].

alg: An identifier for a JWS asymmetric digital signature algorithm from [IANA.JOSE.ALGS]. It MUST NOT be none or an identifier for a symmetric algorithm (Message Authentication Code (MAC)).

jwk: Represents the public key chosen by the client in JSON Web Key (JWK) [RFC7517] format as defined in Section 4.1.3 of [RFC7515]. It MUST NOT contain a private key.

The payload of a DPoP proof MUST contain at least the following claims:

jti: Unique identifier for the DPoP proof JWT. The value MUST be assigned such that there is a negligible probability that the same value will be assigned to any other DPoP proof used in the same context during the time window of validity. Such uniqueness can be accomplished by encoding (base64url or any other suitable encoding) at least 96 bits of pseudorandom data or by using a version 4 Universally Unique Identifier (UUID) string according to [RFC4122]. The jti can be used by the server for replay detection and prevention; see Section 11.1.

htm: The value of the HTTP method (Section 9.1 of [RFC9110]) of the request to which the JWT is attached.

htu: The HTTP target URI (Section 7.1 of [RFC9110]) of the request to which the JWT is attached, without query and fragment parts.

iat: Creation timestamp of the JWT (Section 4.1.6 of [RFC7519]).

When the DPoP proof is used in conjunction with the presentation of an access token in protected resource access (see Section 7), the DPoP proof MUST also contain the following claim:

ath: Hash of the access token. The value MUST be the result of a base64url encoding (as defined in Section 2 of [RFC7515]) the SHA-256 [SHS] hash of the ASCII encoding of the associated access token's value.

When the authentication server or resource server provides a DPoP-Nonce HTTP header in a response (see Sections 8 and 9), the DPoP proof MUST also contain the following claim:

nonce: A recent nonce provided via the DPoP-Nonce HTTP header.

A DPoP proof MAY contain other JOSE Header Parameters or claims as defined by extension, profile, or deployment-specific requirements.

Figure 4 is a conceptual example showing the decoded content of the DPoP proof in Figure 2. The JSON of the JWT header and payload are shown, but the signature part is omitted. As usual, line breaks and extra spaces are included for formatting and readability.

{
"typ":"dpop+jwt",
"alg":"ES256",
"jwk": {
"kty":"EC",
"x":"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y":"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv":"P-256"
}
}
.
{
"jti":"-BwC3ESc6acc2lTc",
"htm":"POST",
"htu":"https://server.example.com/token",
"iat":1562262616
}

Figure 4: Example JWT Content of a DPoP Proof

Of the HTTP request, only the HTTP method and URI are included in the DPoP JWT; therefore, only these two message parts are covered by the DPoP proof. The idea is to sign just enough of the HTTP data to provide reasonable proof of possession with respect to the HTTP request. This design approach of using only a minimal subset of the HTTP header data is to avoid the substantial difficulties inherent in attempting to normalize HTTP messages. Nonetheless, DPoP proofs can be extended to contain other information of the HTTP request (see also Section 11.7).

4.3. Checking DPoP Proofs

To validate a DPoP proof, the receiving server MUST ensure the following:

  1. There is not more than one DPoP HTTP request header field.
  2. The DPoP HTTP request header field value is a single and well-formed JWT.
  3. All required claims per Section 4.2 are contained in the JWT.
  4. The typ JOSE Header Parameter has the value dpop+jwt.
  5. The alg JOSE Header Parameter indicates a registered asymmetric digital signature algorithm [IANA.JOSE.ALGS], is not none, is supported by the application, and is acceptable per local policy.
  6. The JWT signature verifies with the public key contained in the jwk JOSE Header Parameter.
  7. The jwk JOSE Header Parameter does not contain a private key.
  8. The htm claim matches the HTTP method of the current request.
  9. The htu claim matches the HTTP URI value for the HTTP request in which the JWT was received, ignoring any query and fragment parts.
  10. If the server provided a nonce value to the client, the nonce claim matches the server-provided nonce value.
  11. The creation time of the JWT, as determined by either the iat claim or a server managed timestamp via the nonce claim, is within an acceptable window (see Section 11.1).
  12. If presented to a protected resource in conjunction with an access token,
    • ensure that the value of the ath claim equals the hash of that access token, and
    • confirm that the public key to which the access token is bound matches the public key from the DPoP proof.

To reduce the likelihood of false negatives, servers SHOULD employ syntax-based normalization (Section 6.2.2 of [RFC3986]) and scheme-based normalization (Section 6.2.3 of [RFC3986]) before comparing the htu claim.

These checks may be performed in any order.