DPOP - maduvena/jans-docs GitHub Wiki

DPoP - Demonstration of Proof of Possession (at the Application Layer)

  • DPoP is a mecahnism of Proof of Possession (PoP)
  • Implementing DPOP ensures that the client presenting the AT to the RS is the valid owner of the AT
  • PoP using MTLS is a better choice than DPoP, however incase of public clients (native apps) and Single Page App (SPA) - DPoP is the way to go.

DPOP flow (Client to AS)

Sequence diagram for DPOP Flow (DPOP Proof)

title DPOP flow (Client to AS)

Client->Client:1. Generate a key pair. Public, Private

Client->Client:2. Prepare Header (Public Key + Payload + Signature)

Client->Client:3. Sign using Pvt key

Client->Client:4. Call this DPoP proof JWT

Client->Auth Server:5. /token + DPOP Proof JWT in header

Auth Server->Auth Server:6. Extract the DPoP Proof JWT 

Auth Server->Auth Server:7. Verify the signature using the included \npublic key (this also confirms that the\n client has the private key)

Auth Server->Auth Server:8. Generate an AT, bind the public key claimed \nby the client in the DPoP proof\n that is, the access token cannot be used without\n proving possession of the respective private key)


Auth Server->Client:9. return AT

DPoP Proof JWT

JWT Header

{
   "typ":"dpop+jwt",      // type 
   "alg":"ES256",
   "jwk":{            // public key for which PoP is being demonstrated
        "kty":"EC",
        "crv":"P-256",
        "x":"vvpxrhx-55433hRICRDY9zCkDlpBhF42UQUfWVWDDEs",
         "y":"3XE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGXXA"
         }
}

JWT Payload

{
"jti":"-XwS3ESc6acc2lTc", 
"htm":"POST",
"htu":"https://my.jans.server/token",
"iat":177226224
}

AT Request

POST /token HTTP/1.1
Host: my.jans.server.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
DPoP: ey......    // This is the DPoP proof JWT in the header

grant_type=authorization_code
&code=ABCDE
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
&....

AT Response

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store
{
"access_token":"eyJ....."
"token_type":"DPoP", // this means that the AT is bound to the public key  (same as the one in the request header)
"expires_in":3600,
"refresh_token":"ABCDE.....",
}

The Access Token

{
"sub":"[email protected]",
"iss":"https://my.jans.server",
"aud":"https://resource.jans.server",
"nbf":123213132,
"exp":123123123,
"cnf":
    {
      "jkt":"ABCDEF-gggggg-acacacac-BBBBB"  // SHA-256 JWK Thumbprint of public key to which the AT is bound
    }
}

Client requests protected resource

Sequence diagram for Client Request for Protected Resource

title Client requests protected resource (Client to RS)



Client->Client:1. Prepare Header (Public Key + Payload + Signature)

Client->Client:2. Sign using Pvt key, same pvt key from AT flow

Client->Client:3. Call this DPoP proof JWT

Client->Resource Server:4. API call + AT + DPOP Proof JWT in header

Resource Server->Resource Server:5. Extract the DPoP Proof JWT 

Resource Server->Resource Server:7. Verify the signature using the included \npublic key (this also confirms that the\n client has the private key)

Resource Server->Resource Server:8.Check the binding access between the AT and the public key

Resource Server->Client:9. return Protected Resource

The HTTP header of Request to protected resource

GET /protectedresource HTTP/1.1
Host: resource.jans.server
Authorization: DPoP ey....  // the access token that is bound to public key
DPoP: ey.....  // the DPoP proof JWT

Configuring the Jans-auth server to be able to use DPOP

Considerations for the client

Testing and references

  1. https://github.com/JanssenProject/jans/blob/main/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/dpop/DpopTokenRequestHttpTest.java
  2. https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/