DevPortalPagoPA



Tabella dei contenuti

How to request a DPoP voucher for a producer's API (standard)

This tutorial explains how to request a voucher that uses Demonstrating Proof-of-Possession (DPoP) — the IETF standard (RFC 9449) that makes a voucher (JWT token) unusable if stolen, because it is bound to a public key owned by the caller. For more details, see the focus section.
More information about this implementation can be found in the dedicated section.

Summary of the flow

In essence, the end-to-end process requires seven steps:
  1. The consumer generates the standard client assertion; signs it with the private key whose public key is stored in their client on PDND.
  2. The consumer builds the DPoP intended for the PDND authorization server; signs it with a second private key whose public key will be inserted in the DPoP header, in the jwk field.
  3. The consumer requests the voucher from the PDND authorization server, adding the DPoP header.
  4. The PDND authorization server performs the necessary checks. If successful, it returns a DPoP-type voucher.
  5. The consumer builds a second DPoP, this time intended for the resource server, i.e., the producer’s e-service API; signs it with the same private key as the DPoP in step 2, again putting the corresponding public key in the DPoP header, in the jwk field.
  6. The consumer makes a request to the producer’s e-service; inserts both the voucher issued by PDND in the Authorization header, and the DPoP generated in the previous step in the DPoP header.
  7. The producer performs the necessary checks. If successful, it processes the consumer’s request.

Prerequisites

It is assumed that the consumer has:
  • Created an e-service type client (read tutorial).
  • Generated at least one set of cryptographic material and uploaded the related public key to PDND within the client (read tutorial).
  • Associated the client with the purpose for which they want to obtain or send data to the producer (read tutorial).

Step 1 - Generating the client assertion

The first step is to build a valid client assertion. The client assertion is composed of a header and a payload containing the following fields.
Header:
Field nameMeaning
kidthe ID of the key used to sign the assertion, available on PDND
algthe algorithm used to sign the JWT (for now, always RS256)
typthe type of object being sent (always JWT)
Payload:
Field nameMeaning
issthe issuer, in this case the clientId
subthe subject, in this case always the clientId
audthe audience, available on PDND
jtithe JWT ID, a unique random ID assigned by whoever is creating the token, used to track the token itself. It is the caller’s responsibility to ensure that the ID of this token is unique for the client assertion
iatissued at, the timestamp indicating when the token was created, expressed in UNIX epoch (numeric value, not a string)
expexpiration, the timestamp indicating when the token expires, expressed in UNIX epoch (numeric value, not a string)
purposeIdThe ID of the specific purpose for which you want to obtain a voucher, available in the back office
As an example, here is a deserialized client assertion to highlight its contents.
Header:
1{
2  "alg": "RS256",
3  "kid": "2MJFa7aSSveFte8ULX9U-MaaygcoL5fBIJDTXBdba64",
4  "typ": "jwt"
5}
6
Payload:
1{
2  "iss": "8e9f24ca-78f5-4c69-9e4f-0efbeac7bb2b", 
3  "sub": "8e9f24ca-78f5-4c69-9e4f-0efbeac7bb2b",
4  "aud": "auth.interop.pagopa.it/client-assertion",
5  "jti": "23387ac1-c192-4573-8350-207a4213d4be",
6  "iat": 1616170068,
7  "exp": 1616170668,
8  "purposeId": "34f1624b-91cb-4b05-b8c0-cad208a30222"
9}
10
After building a valid client assertion, it must be signed with your private key (the counterpart of the public key uploaded to the client in PDND).
For demonstration purposes, a Python script has been published showing how to perform the operation. All instructions are available in the back office, within your client.
A function is also available to check the validity of your client assertion and highlight any errors. The tool is available in the back office under Developers Tools > Debug client assertion.

Step 2 - Generating the first DPoP

The consumer then builds the DPoP intended for the PDND authorization server, which is a JWT with
Header:
1{
2  "typ": "dpop+jwt",
3  "alg": "ES256",
4  "jwk": "{CALLER_PUBLIC_KEY}"
5}
6
Payload:
1{
2  "htm": "POST",
3  "htu": "https://auth.interop.pagopa.it/token.oauth2",
4  "iat": 1747406361,
5  "jti": "b60203a7-6f31-4d08-a3d1-f69ba308eee0"
6}
7
where
Field nameMeaning
typmust be set to dpop+jwt
algIndicates the algorithm used to sign the DPoP. The recommended algorithm is ES256
jwkThe public key in JWK format corresponding to the private key used to sign the DPoP
htmIndicates the HTTP method being invoked. For obtaining a voucher from PDND, the method is POST
htuIndicates the URL being invoked. For obtaining a voucher from PDND in the Production environment it is https://auth.interop.pagopa.it/token.oauth2 (for Testing and Validation environments use the specific one provided in the back office)
iatIssued at — the timestamp (UNIX epoch, numeric) indicating the date and time when the DPoP is created
jtiUnique identifier of the DPoP. It is the consumer’s responsibility to ensure that the ID of this token is unique and not reused

Step 3 - Requesting the voucher from the authorization server

The third step is to call the PDND authorization server with the signed client assertion to obtain in return a voucher that can be used with the PDND APIs.
In the request header, you must insert a DPoP header containing the DPoP generated in the previous step:
DPoP: <DPoP_proof>
The endpoint URL for the authorization server depends on the environment and will be clearly visible in the back office interface.
The endpoint must be called with the following body parameters:
Field nameMeaning
client_idagain, the clientId used in the assertion
client_assertionthe signed client assertion from the first step
client_assertion_typethe client assertion format, as indicated in RFC (always urn:ietf:params:oauth:client-assertion-type:jwt-bearer)
grant_typethe type of flow used, as indicated in RFC (always client_credentials)

Step 4 - The authorization server verifies and issues the voucher

The PDND authorization server performs the necessary checks, specifically:
  • verifies the client-assertion according to the usual controls;
  • verifies the DPoP signature using the public key in the jwk field of the header;
  • checks that the htm and htu fields match the expected values for the current request;
  • considers a proof valid only if presented within 60 seconds from its creation time (iat);
  • verifies that the jti value has not already been used for another call to the PDND authorization server.
If valid, the PDND authorization server returns a DPoP-type voucher (token_type), signed as a JWT with a header "typ": "at+jwt" and containing a cnf.jkt claim.
Example of server response:
1{
2  "access_token": "eyJ0eXAiOiJhdCtqd3QiLC...",
3  "expires_in": 600,
4  "token_type": "DPoP"
5}
6
If we decode the access_token field, we get
Header:
1{
2  "typ": "dpop+jwt",
3  "alg": "RS256",
4  "use": "sig",
5  "kid": "{PDND_KEY_KID}"
6}
7
Payload:
1{
2  "iss": "interop.pagopa.it", 
3  "nbf": 1747408537,
4  "iat": 1747408537,
5  "exp": 1747409537,
6  "jti": "12297ac1-c192-4573-8350-207a4213e5ac",
7  "aud": "https://eservice.pa.it/api/v1",
8  "sub": "9b361d49-33f4-4f1e-a88b-4e12661f2309",
9  "client_id": "9b361d49-33f4-4f1e-a88b-4e12661f2309",
10  "purposeId": "1b361d49-33f4-4f1e-a88b-4e12661f2300",
11  "producerId" : "0e9e2dab-2e93-4f24-ba59-38d9f11198ca",
12  "consumerId" : "69e2865e-65ab-4e48-a638-2037a9ee2ee7",
13  "eserviceId" : "b8c6d7ad-93fc-4eaf-9018-3cd8bf98163f",
14  "descriptorId": "9525a54b-9157-4b46-8976-ec66f20b7d7e",
15  "cnf": {
16    "jkt" : "L5TP6x6ved3p_jmIAtCiHMcNJeRrGWAusNnQkTTrnLY"
17  }
18}
19
The cnf.jkt field contains the thumbprint of the public key in JWK format (RFC 7638) used in the DPoP sent by the consumer (client) to the PDND authorization server.

Step 5 - The consumer builds a second DPoP

The consumer builds a second DPoP, this time intended for the producer’s e-service APIs. This second DPoP is similar to the one produced in step 2, with two differences:
  • the htm and htu fields must refer to the resource being called on the producer’s server (as specified in the API interface file) instead of the PDND authorization server;
  • another field, ath, must be added.
The ath field contains the hash of the voucher issued by PDND. This hash is obtained using SHA256 and must be Base64URL-encoded, as follows:
1BASE64URL(SHA-256(access_token_bytes))
2

Step 6 - Requesting data from the producer

The voucher must be inserted in the header of all subsequent calls to the producer’s APIs:
1Authorization: DPoP <voucher_issued_by_PDND>
2
The consumer must also insert another header:
DPoP: <DPoP_proof_generated_at_previous_step>

Step 7 - Waiting for the producer’s checks

The producer carries out all necessary checks. If everything is in order, it processes the consumer’s request, returning the requested data in the case of a data-providing e-service, or accepting the data from the consumer in the case of a data-receiving e-service.
To consult the recommended checks for producers, see the dedicated section.

Hai bisogno di aiuto?

Apri un ticket utilizzando l’apposita funzione all’interno della tua Area Riservata

Dicci cosa ne pensi

Per segnalare problemi o dare feedback, puoi aprire una segnalazione su Github