Tabella dei contenuti
Verifichiamo la correttezza delle informazioni di residenza
L'e-service “Attestazione - Residence Verification”, pubblicato sul catalogo, consente di verificare la presenza e la correttezza di un determinato indirizzo fisico simulando un ente che possiede le informazioni aggiornate e centralizzate di tutti gli indirizzi di residenza/domicili fisici dei soggetti.
In questo tutorial vedremo un caso reale di applicazione di questo servizio.
Il caso d'uso
Come fruitore, ho la necessità di verificare la correttezza delle informazioni presenti sulla mia base dati relative agli indirizzi fisici dei soggetti. Per procedere, dovrò effettuare la sottoscrizione all'e-service “Attestazione - Residence Verification”, che consente di recuperare questi dati grazie all'invocazione del seguente set di API:
POST /residence-verification
POST /residence-verification/check
Data preparation
La prima cosa da fare è la configurazione dei dati. Procediamo dunque, per la prima volta, alla fase di Data Preparation.
Facendo riferimento al problema sopra esposto, supponiamo di avere la seguente base dati all'interno della nostra applicazione
ID | Nome | Cognome | CAP | Città |
---|---|---|---|---|
RSSMRA80A01H501U | Mario | Rossi | 00100 | Roma |
LGUBCH80A01H501B | Luigi | Bianchi | NULL | NULL |
In accordo a questa effettuiamo la data preparation simulando il seguente scenario:
- L'id RSSMRA80A01H501U è un soggetto noto a cui è associato l'indirizzo di residenza ed è ancora valido
- L'id LGUBCH80A01H501B è un soggetto noto per il quale però non siamo a conoscenza dell'attuale indirizzo di residenza.
Replichiamo la configurazione desiderata nel seguente modo:
POST /residence-verification/data-preparation
Header:
1Content-Type: application/json
2Authorization: Bearer {{bearerToken}}
3x-correlation-id: {{myUniqueCorrelationId}}
4
Payload:
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}
26
Response:
1application/json
2{
3 "uuid": "2c41e733-e9ae-4b69-9243-6217d4cf26e3"
4}
5
Status codes:
200 - Configurazione salvata con successo
Procediamo con il censimento anche del secondo soggetto, simulando che l'erogatore sia a conoscenza dell'indirizzo.
Header:
1Content-Type: application/json
2Authorization: Bearer {{bearerToken}}
3x-correlation-id: {{myUniqueCorrelationId}}
4
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}
5
Status codes:
200 - Configurazione salvata con successo
Dato il nostro scenario abbiamo completato la fase di configurazione.
Generazione dei Token Agid
Agid-JWT-TrackingEvidence
Per poter procedere all'invocazione delle API, l'utente deve presentare anche il token di audit "Agid-JWT-TrackingEvidence", come richiesto dal pattern "AUDIT_REST_01"
Il pattern garantisce i seguenti punti:
- L'autenticità della comunicazione tra il servizio erogato e ciascun utente è assicurata tramite la sicurezza a livello di messaggio, seguendo il pattern "ID_AUTH_REST_01 via PDND".
- Le informazioni di audit necessarie al fornitore per identificare l'origine specifica di ogni richiesta di accesso ai dati effettuata dall'utente sono incluse in un token di audit conforme al pattern "AUDIT_REST_01". Queste informazioni vengono trasmesse dall'applicazione dell'utente attraverso l'header HTTP.
Di seguito riportiamo le linee guida utili alla generazione del suddetto token:
- Utilizzare una libreria JWT: a seconda del linguaggio di programmazione scelto è possibile utilizzare differenti librerie. Per fare un esempio è possibile usare una libreria come jsonwebtoken per Node.js oppure Java, pyjwt per Python, o altre librerie JWT disponibili nei vari linguaggi di programmazione.
- Definire il payload del token: Il payload del JWT deve contenere le informazioni richieste dal pattern "AUDIT_REST_01":
- iat (Issued At): la data e l'ora in cui il token è stato emesso, espressa in secondi.
- exp (Expiration): la data e l'ora di scadenza del token.
- sub (Subject): l'identificativo del soggetto, ovvero il clientId censito su PDND.
- iss(Issuer): l'identificativo del soggetto, ovvero il clientId censito su PDND.
- aud(Audience): l'identificativo dell'audience, reperibile sempre nella sezione dedicata al tuo client;
- purposeId: rappresenta l'id della finalità
- jti (JWT ID): un identificativo univoco del token.
- Firmare il token: Dopo aver creato il payload, è necessario firmare il token con la chiave privata caricata sulla PDND in fase di registazione del client (riferimento al paragrafo https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guida+Operativa+Ambiente+Attestazione#3.4.1-Come-generare-il-Voucher).
La firma deve essere generata utilizzando un algoritmoRS256 (RSA con SHA-256) e chiave privata PKCS#8. La chiave privata utilizzata per la firma deve essere quella associata alla chiave pubblica registrata sulla PDND. - Generare il JWT: Una volta preparati il payload e la firma, non resta che utilizzare la libreria JWT per la creazione del token.
- Includere il token nell'header della richiesta: Una volta generato, il token "Agid-JWT-TrackingEvidence" va inserito nell'header HTTP della richiesta.
Se tutti i passaggi sopra riportati sono stati eseguiti correttamente l'erogatore verificherà correttamente il token e risponderà con successo alla richiesta.
Agid-JWT-Signature
L'AgID-JWT-Signature, in maniera analoga al precedente è un token JSON Web Token (JWT).
Lo scopo di tale token è quello di garantire l'integrità e l'autenticità delle comunicazioni, in conformità con le linee guida dettate dall'Agenzia per l'Italia Digitale (AgID).
Nello specifico, implementa il pattern model “INTEGRITY_REST_02”.
Di seguito riportiamo le linee guida utili alla generazione del suddetto token:
- Utilizzare una libreria JWT: a seconda del linguaggio di programmazione scelto è possibile utilizzare differenti librerie. Per fare un esempio è possibile usare una libreria come jsonwebtoken per Node.js oppure Java, pyjwt per Python, o altre librerie JWT disponibili nei vari linguaggi di programmazione.
- Definire il payload del token: Il payload del JWT deve contenere le informazioni richieste dal pattern "AUDIT_REST_01":
- iat (Issued At): la data e l'ora in cui il token è stato emesso, espressa in secondi.
- exp (Expiration): la data e l'ora di scadenza del token.
- sub (Subject): l'identificativo del soggetto, ovvero il clientId censito su PDND.
- iss(Issuer): l'identificativo del soggetto, ovvero il clientId censito su PDND.
- aud(Audience): l'identificativo dell'audience, reperibile sempre nella sezione dedicata al tuo client;
- kid: l'id della chiave che si usa per firmare l'asserzione, reperibile all'interno del tuo client, sotto la sezione “Client assertion”
- jti (JWT ID): un identificativo univoco del token.
- signed_header: contiene il digest del contenuto (hash dei dati) calcolato con l'algoritmo SHA-256 e il tipo di contenuto, impostato (ad esempio application/json).
Relativamente a questo campo, riportiamo di seguito uno snippet di codice esplicativo per la sua valorizzazione
Esempio in Java:
1import java.security.MessageDigest;
2import java.util.Base64;
3import java.util.HashMap;
4import java.nio.charset.StandardCharsets;
5
6// jsonInputString è la request su cui calcolo il digest
7String jsonInputString = "My request";
8
9try {
10 // Calcolo del digest utilizzando 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 // Costruzione delle due mappe da settare come claims
16 HashMap<String, String> m = new HashMap<String, String>();
17 // Setto la request sopra calcolata
18 m.put("digest", "SHA-256=" + encodedBody);
19
20 HashMap<String, String> m2 = new HashMap<String, String>();
21 // Setto il content type della richiesta
22 m2.put("content-type", "application/json");
23
24 // Valorizzo il claim con le mappe sopra valorizzate
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 }
34
Il payload ottenuto a seguito di questo step avrà una forma simile alla seguente:
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
- Firmare il token: Dopo aver creato il payload, è necessario firmare il token con la chiave privata caricata sulla PDND in fase di registazione del client (riferimento al paragrafo https://pagopa.atlassian.net/wiki/spaces/ADA/pages/1289945113/Guida+Operativa+Ambiente+Attestazione#3.4.1-Come-generare-il-Voucher).
La firma deve essere generata utilizzando un algoritmoRS256 (RSA con SHA-256) - Generare il JWT: Una volta preparati il payload e la firma, non resta che utilizzare la libreria JWT per la creazione del token.
- Includere il token nell'header della richiesta: Una volta generato, il token "Agid-JWT-Signature" va inserito nell'header HTTP della richiesta.
Se tutti i passaggi sopra riportati sono stati eseguiti correttamente l'erogatore verificherà correttamente il token e risponderà con successo alla richiesta.
Invocazione e-service per verifica informazioni
Adesso che abbiamo generato tutti i token previsti dai controlli di sicurezza del servizio, possiamo procedere alla verifica delle informazioni presenti sulla nostra base dati. Per il soggetto “Mario Rossi”, del quale conosciamo già l’indirizzo di residenza e dobbiamo verificare la corrispondenza delle informazioni, invochiamo la seguente 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
38
Response:
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
70
Status codes:
200 - Richiesta effettuata con successo
Dalla response ricevuta deduciamo che le informazioni presenti sulla nostra base dati sono effettivamente coerenti con quanto detenuto dall'erogatore
Invocazione e-service per richiesta informazioni
Per il soggetto “Luigi Bianchi”, del quale non conosciamo l'indirizzo di residenza, invochiamo la seguente 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}'
13
Response:
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}
113
Status codes:
200 - Richiesta effettuata con successo
Dalla response ricevuta possiamo arricchire la nostra base dati con le informaizoni ricevute.
Esito Finale
Dopo aver interrogato l'e-service possiamo procedere all'aggiornamento della nostra base dati in base alle informazioni che abbiamo recuperato.
Di seguito una panoramica della situazione a seguito dell'aggiornamento
ID | Nome | Cognome | CAP | Città |
---|---|---|---|---|
RSSMRA80A01H501U | Mario | Rossi | 00100 | Roma |
LGUBCH80A01H501B | Luigi | Bianchi | 10024 | Torino |
La nostra base dati è stata correttamente aggiornata e abbiamo arricchito il soggetto Luigi Bianchi con le informazioni mancanti.
Hai bisogno di aiuto?
Apri un ticket utilizzando l’apposita funzione all’interno della tua Area Riservata