Table of contents
Checking the correctness of the residence information
The “Certification - Residence Verification” e-service published in the catalog makes it possible to verify the presence and correctness of a certain physical address, simulating an institution that has all the updated and centralized information related to all the physical residence/domicile addresses of the subjects.
This tutorial shows an actual case of using this service.
The use case
As a user, I need to verify the correctness of the information present in my database related to the physical addresses status of the subjects. To do so, I must subscribe to the “Certification - Residence Verification” e-service, which makes it possible to recover this data through the invocation of the following set of APIs:
POST /residence-verification
POST /residence-verification/check
Data preparation
The first thing to do is to configure the data. Therefore we will proceed, for the first time, with the Data Preparation phase.
In reference to the problem indicated above, in this example we have the following database in our application
| ID | Name | Surname | ZIP CODE | City |
|---|---|---|---|---|
| RSSMRA80A01H501U | Mario | Rossi | 00100 | Rome |
| LGUBCH80A01H501B | Luigi | Bianchi | NULL | NULL |
Based on this, data preparation is performed by simulating the following scenario:
- The ID RSSMRA80A01H501U is a known subject that is associated with the address of residence and is still valid
- The ID LGUBCH80A01H501B is a known subject for which we do not know the current address of residence.
We can replicate the desired configuration as follows:
POST /residence-verification/data-preparation
Header:
1Content-Type: application/json
2Authorization: Bearer {{bearerToken}}
3x-correlation-id: {{myUniqueCorrelationId}}
4Payload:
1application/json
2{
3 "subject": {
4 "subjectId": "RSSMRA80A01H501U",
5 "id": "123",
6 "surname": "Rossi",
7 "name": "Mario",
8 "gender": "M"
9
10 },
11 "address": {
12 "addressType": "Via",
13 "noteaddress": "N/D",
14 "address": {
15 "cap": "00100",
16 "municipality": {
17 "nameMunicipality": "Roma",
18 "istatCode": "123456",
19 "acronymIstatProvince": "RM",
20 "placeDescription": "N/D"
21 }
22 },
23 "addressStartDate": "2024-01-01"
24 }
25}
26Response:
1application/json
2{
3 "uuid": "2c41e733-e9ae-4b69-9243-6217d4cf26e3"
4}
5Status codes:
200 - Configuration saved successfully
The second subject can now be registered, simulating that the provider knows the address.
Header:
Content-Type: application/json Authorization: Bearer {{bearerToken}} x-correlation-id: {{myUniqueCorrelationId}}
Payload:
application/json
{
"subject": {
"subjectId": "LGUBCH80A01H501B",
"id": "1234",
"surname": "Bianchi",
"name": "Luigi",
"gender": "M"
},
"address": {
"addressType": "Via",
"noteaddress": "N/D",
"address": {
"cap": "10024",
"municipality": {
"nameMunicipality": "Torino",
"istatCode": "123456",
"acronymIstatProvince": "TO",
"placeDescription": "N/D"
}
},
"addressStartDate": "2024-01-01"
}
}
Response:
1application/json
2{
3 "uuid": "bc830947-4772-44ce-94eb-7c8c538f785c"
4}
5Status codes:
200 - Configuration saved successfully
Given our scenario, we have completed the configuration phase.
Generation of the Agid tokens
Agid-JWT-TrackingEvidence
To proceed with the invocation of the APIs, the user must present also the audit token "Agid-JWT-TrackingEvidence", as required by the pattern "AUDIT_REST_01"
The pattern guarantees the following points:
- The authenticity of the communication between the provided service and each user is associated by means of security on a message level, following the pattern "ID_AUTH_REST_01 via PDND".
- The audit information necessary for the supplier to identify the specific origin of each data access request performed by the user is included in an audit token that conforms with the pattern "AUDIT_REST_01". This information is transmitted by the user application to the HTTP header.
The guidelines to be used for the generation of the said token are provided below:
- Use a JWT library: depending on the selected programming language, it is possible to use different libraries. For example, it is possible to use a library such as jsonwebtoken for Node.js or Java, pyjwt for Python, or other JWT libraries available in the various programming languages.
- Define the token payload: The JWT payload must contain the information required by the pattern "AUDIT_REST_01":
- iat (Issued At): the date and time the token was issued, expressed in seconds.
- exp (Expiration): the date and time of token expiration.
- sub (Subject): the identifier of the subject, that is the clientId registered in PDND.
- iss (Issuer): the identifier of the subject, that is the clientId registered in PDND.
- aud(Audience): the identifier of the audience, can also be found in the section dedicated to your client;
- purposeId: the ID of the purpose
- jti (JWT ID): a univocal identifier of the token.
- Sign the token: After creating the payload, the token must be signed using the private key uploaded to PDND during client registration (reference to paragraph (https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guide+Operative+Environment+Certification#3.4.1-How to generate the voucher)https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guida+Operativa+Ambiente+Attestazione#3.4.1-Come-generare-il-Voucher).
The signature must be generated using an algorithm RS256 (RSA with SHA-256) and private key PKCS#8. The private key used for the signature must be the one associated with the public key registered on PDND. - Generate the JWT: Once the payload and signature are prepared, the JWT library can be used to create the token.
- Include the token in the header of the request: Once generated the token "Agid-JWT-TrackingEvidence" must be inserted in the HTTP header of the request.
If all the steps indicated above have been performed correctly, the provider will correctly verify the token and respond successfully to the request.
Agid-JWT-Signature
The AgID-JWT-Signature, similar to the previous one, is a JSON Web Token (JWT).
The purpose of this token is to guarantee the integrity and authenticity of the communications, in compliance with the guidelines dictated by the Agency for Digital Italy (AgID).
Specifically, it implements the pattern model “INTEGRITY_REST_02”.
The guidelines to be used for the generation of the said token are provided below:
- Use a JWT library: depending on the selected programming language, it is possible to use different libraries. For example, it is possible to use a library such as jsonwebtoken for Node.js or Java, pyjwt for Python, or other JWT libraries available in the various programming languages.
- Define the token payload: The JWT payload must contain the information required by the pattern "AUDIT_REST_01":
- iat (Issued At): the date and time the token was issued, expressed in seconds.
- exp (Expiration): the date and time of token expiration.
- sub (Subject): the identifier of the subject, that is the clientId registered in PDND.
- iss (Issuer): the identifier of the subject, that is the clientId registered in PDND.
- aud(Audience): the identifier of the audience, can also be found in the section dedicated to your client;
- kid: the ID of the key used to sign the assertion_,_ which can be found in your client area, under the section “Client assertion”
- jti (JWT ID): a univocal identifier of the token.
- signed_header: contains the digest of the content (data hash) calculated with the algorithm SHA-256 and the type of content, set (for example application/json).
An explanatory snippet of code is shown below for setting this field
Example in Java:
1import java.security.MessageDigest;
2import java.util.Base64;
3import java.util.HashMap;
4import java.nio.charset.StandardCharsets;
5
6// jsonInputString is the request used to calculate the digest
7String jsonInputString = "My request";
8
9try {
10// Calculate the digest using SHA-256
11 MessageDigest digest = MessageDigest.getInstance("SHA-256");
12 byte[] hash = digest.digest(jsonInputString.getBytes(StandardCharsets.UTF_8));
13 String encodedBody = Base64.getEncoder().encodeToString(hash);
14
15 // Creation of two maps to set as claims
16 HashMap<String, String>m = new HashMap<String, String>();
17 // Set the request calculated above
18 m.put("digest", "SHA-256=" + encodedBody);
19
20 HashMap<String, String>m2 = new HashMap<String, String>();
21 // Set the content type of request
22 m2.put("content-type", "application/json");
23
24 // Use the maps created above HashMap for the claim
25 HashMap<String, Object> claims = new HashMap<String, Object>();
26 claims.put("signed_headers", new Object[] {m, m2});
27
28 // Stampa per verificare il contenuto dei claims
29 System.out.println("Claims: " + claims);
30
31} catch (Exception e) {
32 e.printStackTrace();
33 }
34The form of the payload obtained following this step will be similar to the following:
1{
2 "aud": "https://{{host}}/residence-verification",
3 "sub": "8532de2b-386f-4aac-adfc-e46d334d3ad0",
4 "nbf": 1729853626,
5 "iss": "8532de2b-386f-4aac-adfc-e46d334d3ad0",
6 "signed_headers": [
7 {
8 "digest": "SHA-256=fb1kol0gIrP3ZxA9k0B/Z8Yt9hDBBVhWRVGU8ilWe8Q="
9 },
10 {
11 "content-type": "application/json"
12 }
13 ],
14 "exp": 1730453626,
15 "iat": 1729853626,
16 "jti": "dd37e0e6-e22d-40c1-a23f-081a8abe123a"
17}
18- Sign the token: After creating the payload, the token must be signed using the private key uploaded to PDND during client registration (reference to paragraph (https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guide+Operative+Environment+Certification#3.4.1-How to generate the voucher)https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guida+Operativa+Ambiente+Attestazione#3.4.1-Come-generare-il-Voucher).
The signature must be generated using an algorithm RS256 (RSA with SHA-256) - Generate the JWT: Once the payload and signature are prepared, the JWT library can be used to create the token.
- Include the token in the header of the request: Once generated, the token "Agid-JWT-Signature" must be inserted in the HTTP header of the request.
If all the steps indicated above have been performed correctly, the provider will correctly verify the token and respond successfully to the request.
E-service invocation for information verification
Now that all the tokens required by the service security checks have been generated, we can verify the information present in our database. For the subject “Mario Rossi”, whose residence address we know and for whom we have to check the correspondence of the information, we invoke the following API:
POST /residence-verification/check
1curl --location '{host}/residence-verification/check' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'x-correlation-id: {{myUniqueCorrelationId}}' \
5--header 'Agid-JWT-Signature: {{AgidJWTSignature}}' \
6--header 'Agid-JWT-TrackingEvidence: {{AgidJWTTrackingEvidence}}' \
7--header 'Authorization: Bearer {{bearerToken}}' \
8--data '{
9 "operationId": "123",
10 "criteria": {
11 "subjectId": "RSSMRA80A01H501U",
12 "id": "123",
13 "surname": "Rossi",
14 "name": "Mario",
15 "gender": "M"
16 },
17 "requestData": {
18 "dateOfRequest": "2024-01-01",
19 "useCase": "my test",
20 "motivation": "Check information"
21 },
22 "check": {
23 "address": {
24 "addressType": "Via",
25 "address": {
26 "cap": "00100",
27 "municipality": {
28 "nameMunicipality": "Roma",
29 "istatCode": "123456",
30 "acronymIstatProvince": "RO",
31 "placeDescription": "N/D"
32 }
33 }
34 }
35 }
36}'
37
38Response:
1application/json
2{
3 "idOp": "123",
4 "subjects": {
5 "infoSubject": [
6 {
7 "infoInstitution": [
8 {
9 "id": "94cfacfe-7beb-4c8f-bf4b-3c71207903c7",
10 "chiave": "addressType",
11 "valore": "S",
12 "valoreTesto": "Via",
13 "valoreData": "2024-9-5",
14 "dettaglio": "-"
15 },
16 {
17 "id": "faf003f7-3e2f-4b5d-967c-d9ad404d198e",
18 "chiave": "address",
19 "valore": "N",
20 "valoreTesto": {
21 "cap": "00100",
22 "municipality": {
23 "nameMunicipality": "Roma",
24 "istatCode": "123456",
25 "acronymIstatProvince": "RM",
26 "placeDescription": "N/D"
27 },
28 "fraction": "",
29 "toponym": {
30 "codType": "",
31 "type": "",
32 "originType": "",
33 "toponymCod": "",
34 "toponymDenomination": "",
35 "toponymSource": ""
36 },
37 "civicNumber": {
38 "civicCod": "",
39 "civicSource": "",
40 "civicNumber": "",
41 "metric": "",
42 "progSNC": "",
43 "letter": "",
44 "exponent1": "",
45 "color": "",
46 "internalCivic": {
47 "court": "",
48 "stairs": "",
49 "internal1": "",
50 "espInternal1": "",
51 "internal2": "",
52 "espInternal2": "",
53 "externalStairs": "",
54 "secondary": "",
55 "floor": "",
56 "nui": "",
57 "isolated": ""
58 }
59 }
60 },
61 "valoreData": "2024-9-5",
62 "dettaglio": "-"
63 }
64 ]
65 }
66 ]
67 }
68}
69
70Status codes:
200` ``- Request made successfully””
From the received response, we can see that the information present in our database is coherent with the provider information
E-service invocation for information request
For the subject “Luigi Bianchi”, whose address of we do not know, we invoke the following API:
POST /residence-verification
1curl --location '{{
2 "operationId": "1",
3 "criteria": {
4 "subjectId": "LGUBCH80A01H501B",
5 "id": "1234"
6 },
7 "requestData": {
8 "dateOfRequest": "2024-11-01",
9 "useCase": "my test",
10 "motivation": "retrieve information"
11 }
12}'
13Response:
1application/json
2{
3 "idOp": "1",
4 "subjects": {
5 "subject": [
6 {
7 "generality": {
8 "subjectId": {
9 "subjectId": "LGUBCH80A01H501B",
10 "subjectIdValidity": "",
11 "dataAttributionValidity": ""
12 },
13 "surname": "Bianchi",
14 "noSurname": "false",
15 "name": "Luigi",
16 "noName": "false",
17 "gender": "M",
18 "birthDate": "",
19 "noDay": "",
20 "noMonth": "",
21 "birthPlace": {
22 "exceptionalPlace": "",
23 "municipality": {
24 "nameMunicipality": "",
25 "istatCode": "",
26 "acronymIstatProvince": "",
27 "placeDescription": ""
28 },
29 "place": {
30 "placeDescription": "",
31 "countryDescription": "",
32 "codState": "",
33 "provinceCounty": ""
34 }
35 },
36 "AIRESubject": "",
37 "yearExpatriation": "",
38 "idSubjectData": "",
39 "note": ""
40 },
41 "address": [
42 {
43 "addressType": "Via",
44 "noteaddress": "N/D",
45 "address": {
46 "cap": "10024",
47 "municipality": {
48 "nameMunicipality": "Torino",
49 "istatCode": "123456",
50 "acronymIstatProvince": "TO",
51 "placeDescription": "N/D"
52 },
53 "fraction": "",
54 "toponym": {
55 "codType": "",
56 "type": "",
57 "originType": "",
58 "toponymCod": "",
59 "toponymDenomination": "",
60 "toponymSource": ""
61 },
62 "civicNumber": {
63 "civicCod": "",
64 "civicSource": "",
65 "civicNumber": "",
66 "metric": "",
67 "progSNC": "",
68 "letter": "",
69 "exponent1": "",
70 "color": "",
71 "internalCivic": {
72 "court": "",
73 "stairs": "",
74 "internal1": "",
75 "espInternal1": "",
76 "internal2": "",
77 "espInternal2": "",
78 "externalStairs": "",
79 "secondary": "",
80 "floor": "",
81 "nui": "",
82 "isolated": ""
83 }
84 }
85 },
86 "foreignState": {
87 "foreignAddress": {
88 "cap": "",
89 "place": {
90 "placeDescription": "",
91 "countryDescription": "",
92 "countryState": "",
93 "provinceCounty": ""
94 },
95 "toponym": {
96 "denomination": "",
97 "civicNumber": ""
98 }
99 },
100 "consulate": {
101 "consulateCod": "",
102 "consulateDescription": ""
103 }
104 },
105 "presso": "",
106 "addressStartDate": "2024-01-01"
107 }
108 ]
109 }
110 ]
111 }
112}
113Status codes:
200- Request made successfully
From the received response, we can add the received information to our database.
Final result
After querying the e-service, we can update our database based on the information we recovered.
An overview of the situation following the update is shown below
| ID | Name | Surname | ZIP CODE | City |
|---|---|---|---|---|
| RSSMRA80A01H501U | Mario | Rossi | 00100 | Rome |
| LGUBCH80A01H501B | Luigi | Bianchi | 10024 | Turin |
Our database has been updated correctly and we have added the missing information to the subject Luigi Bianchi.