DPoP



Demonstrating Proof of Possession (DPoP) is a method to sender constraint tokens. It means the client that received the token from AuthServer at the token endpoint, is the only one that can send the token to protected resources.


Table 1: Specifications
NameDescription
DPoPSpecification for sender constraining tokens using DPoP

Public and Confidential clients can use DPoP and is an optional addition to increase security and reduce misuse of tokens.


It is possible for public clients to use DPoP because the private key used for signing DPoP tokens is not associated with the client metadata used by AuthServer.

If a client uses DPoP then it is required in the entire flow when retrieving an access token. For the "authorization_code" grant type it means DPoP must be used during authorize and at the token endpoint. When requesting protected resources using a DPoP bound access token, it is always required to present a DPoP proof.


Access tokens and Refresh tokens can be DPoP bound.

Only public clients will have their refresh tokens bound, because confidential clients have their refresh token bound using client authentication.

Even though DPoP is optional at AuthServer, then a client can require DPoP usage through its client metadata. Then if an endpoint that can accept DPoP usage, does not receive DPoP parameters in requests associated with the client, the request will fail.


The DPoP token is a structured JWT and is signed using one of the algorithms from the discovery endpoint. The token can contain the fields in table 2.


Table 2: DPoP token fields
NameDescription
htmHTTP method used at the request. This is a required claim.
htuHTTP absolute URL used at the request. This is a required claim.
nonceServer provided nonce. This is a required claim.
athHash of the access token that is bound to the DPoP token. This is a required claim when requests use the bound access token.
jwkPublic JsonWebKey used to verify the signature of the DPoP token. This is a required header.
typType of JsonWebToken. This is a required header and must be dpop+jwt.
jtiUnique identifier of the DPoP token. This is a required claim.
expUnix timestamp when the DPoP token expires. This is a required claim.
iatUnix timestamp when the DPoP token was issued. This is a required claim.
nbfUnix timestamp when the DPoP token is active from. This is a required claim.

The following JSON example shows a base64 decoded DPoP token sent to the token endpoint.


            
                
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk":
  {
    "kty": "EC",
    "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
    "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
    "crv": "P-256"
  }
}
.
{
  "jti": "b1a4262d-89fb-4d5a-b662-f1b637241a88",
  "htm": "POST",
  "htu": "https://idp.authserver.dk/connect/token",
  "nonce": "eyJ7S_zG.eyJH0-Z.HX4w-7v",
  "iat": 1562262616,
  "nbf": 1562262616,
  "exp": 1562262676
}
        
            
        

The following JSON example shows a base64 decoded DPoP token sent to a protected resource.

            
                
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk":
  {
    "kty": "EC",
    "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
    "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
    "crv": "P-256"
  }
}
.
{
  "jti": "b1a4262d-89fb-4d5a-b662-f1b637241a88",
  "htm": "GET",
  "htu": "https://api.authserver.dk/data",
  "nonce": "eyJ7S_zG.eyJH0-Z.HX4w-7v",
  "ath":"fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo",
  "iat": 1562262616,
  "nbf": 1562262616,
  "exp": 1562262676
}
        
            
        

The DPoP nonce is generated by the server and is used to make sure DPoP tokens cannot be made for the future, if the private key is leaked.

The DPoP nonce is provided through error responses, when a DPoP token contains a missing nonce or a stale nonce.

The lifetime of DPoP nonces are controlled by the client through its metadata.

An example of a response containing a DPoP nonce can be seen below.

            
                
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-cache, no-store
DPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v

{
  "error": "use_dpop_nonce",
  "error_description": "nonce is required for dpop"
}