Utilizzare i voucher
Last updated
Last updated
L'intero ciclo di vita dei voucher si compone di una serie di interazioni machine to machine. Il flusso è diverso se l'aderente sta richiedendo un voucher da spendere su un e-service del catalogo in qualità di fruitore o se vuole dialogare con le API di PDND Interoperabilità per ottenere informazioni.
I voucher sono di fatto dei token JWT. Il flusso di autenticazione implementato è un OAuth 2.0 che fa riferimento all' per quanto riguarda l'uso del Bearer token e all' per l'autorizzazione del client tramite client assertion.
Il paradigma utilizzato per l'implementazione di tutte le API esposte di PDND Interoperabilità è REST.
L'aderente costruisce una client assertion e la firma con una chiave privata la cui omologa pubblica è nota a PDND Interoperabilità. Successivamente, chiama il server autorizzativo di PDND Interoperabilità richiedendo un voucher valido per quell'asserzione firmata. In caso tutti i controlli diano riscontro positivo, PDND Interoperabilità restituisce un voucher all'aderente.
L'aderente fa richiesta di un voucher. Una volta ottenuto, lo inserisce come header autorizzativo nelle successive chiamate verso le API di PDND Interoperabilità.
Il fruitore fa richiesta di un voucher. Una volta ottenuto, lo inserisce come header autorizzativo nelle chiamate che effettua verso l'e-service di un erogatore.
L'erogatore, ricevuta la richiesta con il voucher, effettua i debiti controlli. È possibile che alcuni di questi controlli richiedano il recupero di informazioni detenute da Interoperabilità. In quel caso, l'erogatore stesso dovrà richiedere un voucher spendibile presso le API di Interoperabilità. Una volta terminati i controlli, l'erogatore autorizza il fruitore all'accesso al servizio per la specifica finalità richiesta.
Si suggerisce di fare sempre riferimento alla guida passo passo implementata nel back office di PDND Interoperabilità. Alcune informazioni sono tuttavia riportate anche qui per completezza.
Il primo passo è costruire una client assertion valida e firmarla con la propria chiave privata (che deve essere l'omologa della chiave pubblica depositata sul client su PDND Interoperabilità). La client assertion è composta da un header e un payload.
Nell'header andranno inseriti tre campi:
kid
: l'id della chiave che si usa per firmare l'asserzione, reperibile su PDND Interoperabilità;
alg
: l'algoritmo usato per firmare il JWT (per ora, sempre RS256
);
typ
: il tipo di oggetto che si sta inviando (sempre JWT
).
Nel payload ci saranno invece sei campi:
iss
: l'issuer, in questo caso il clientId;
sub
: il subject, in questo caso sempre il clientId;
aud
: l'audience, reperibile su PDND Interoperabilità;
jti
: il JWT ID, un id unico random assegnato da chi vuole creare il token, si usa per tracciare il token stesso. Deve essere cura del chiamante assicurarsi che l'id di questo token sia unico per quanto riguarda la client assertion;
Una volta firmata l'asserzione, prendere l'output e tenerlo da parte.
Il secondo passaggio è chiamare il server autorizzativo di PDND Interoperabilità con la client assertion per ottenerne in cambio un voucher spendibile presso l'API di di PDND Interoperabilità (ossia un token valido). L'URL dell'endpoint cambia in funzione dell'ambiente e sarà chiaramente visibile sull'interfaccia all'interno del back office. L'endpoint andrà chiamato con alcuni parametri in body:
client_id
: di nuovo il clientId usato nell'assertion;
client_assertion
: il contenuto dell'asserzione firmata nel primo passaggio;
client_assertion_type
: il formato della client assertion, come indicato in RFC (sempre urn:ietf:params:oauth:client-assertion-type:jwt-bearer
);
grant_type
: la tipologia di flusso utilizzato, come indicato in RFC (sempre client_credentials
).
Se tutto è impostato correttamente, PDND Interoperabilità risponderà con un voucher valido all'interno del body della risposta alla proprietà access_token
. Sempre nella risposta, sarà contenuta anche la durata di validità del token in secondi (es. "expires_in": 600
indica un token con validità 10 minuti, 10 * 60 secondi = 600).
Il token andrà inserito nell'header di tutte le chiamate successive verso le API gateway di PDND Interoperabilità come header Authorization: Bearer
.
Le istruzioni sono essenzialmente uguali a quelle del paragrafo precedente, con due eccezioni.
La seconda è che all'interno del payload della client assertion va specificato anche il purposeId
, ossia l'id della finalità per la quale si richiede il voucher. Questo parametro è disponibile nel back office di PDND Interoperabilità.
L'erogatore di un e-service deve ovviamente poter verificare la legittimità di qualsiasi richiesta riceva. Prima di tutto, estrae il voucher dalla richiesta, dall'header Authorization: Bearer
nel quale arriva, e lo deserializza.
A titolo esemplificativo, di seguito un esempio di contenuto di voucher deserializzato.
Header:
Payload:
Il voucher deve essere di tipo at+jwt
.
All'interno del file, l'erogatore cerca l'oggetto che ha lo stesso kid
presente nell'header del voucher. In quello stesso oggetto troverà la chiave pubblica al parametro n
. Effettuerà dunque una verifica della firma, che la chiave privata usata per firmare il voucher corrisponda a quella pubblica appena ottenuta.
Quelli che interessano ai fini della verifica sono:
iss
: l'issuer del voucher, che deve rappresentare il dominio corrispondente all'authorization server di PDND Interoperabilità che ha rilasciato il voucher stesso (ad esempio, l'issuer di produzione è interop.pagopa.it
)
exp
: la scadenza del voucher
aud
: l'audience, ossia l'indicazione di quale servizio dell'erogatore il fruitore intenda consumare con il voucher
Il parametro purposeId
dà il riferimento della finalità per la quale il fruitore fa richiesta all'erogatore. Attraverso successive chiamate all'API gateway di PDND Interoperabilità è possibile richiedere tutte le informazioni di contesto, in caso siano necessarie (ossia i client associati, la richiesta di fruizione e l'e-service di riferimento, ecc).
Per l'erogatore può essere necessario ottenere informazioni aggiuntive che non fanno parte dei campi standard previsti da PDND Interoperabilità all'interno della client assertion. Un esempio può essere l'indirizzo IP del chiamante, oppure informazioni relative all'operatore che sta effettuando la richiesta.
Il meccanismo messo a punto permette all'erogatore di verificare che il fruitore abbia depositato una traccia su PDND Interoperabilità, che agisce da notaio in questo processo. Allo stesso tempo, le informazioni sono scambiate direttamente tra il fruitore e l'erogatore, senza che PDND Interoperabilità ne sia a conoscenza.
il fruitore calcola l'hash: il fruitore calcola un hash non reversibile a partire dal JWS
secondo l'algoritmo di hashing SHA256
;
il fruitore costruisce la client assertion: il fruitore inserisce l'hash nel campo digest
nella client assertion, per il resto compilata come nel flusso standard;
il fruitore richiede un voucher a PDND Interoperabilità: il fruitore inoltra a PDND Interoperabilità la richiesta per ottenere un voucher sulla base della client assertion, come da flusso standard;
PDND Interoperabilità restituisce un voucher al fruitore: PDND Interoperabilità rilascia un voucher al fruitore come nel flusso standard. Inoltre, il voucher rilasciato riporta al suo interno anche l'hash che il fruitore ha inserito nel campo digest
client assertion inviata nella richiesta;
il fruitore fa una richiesta di dati all'erogatore: il fruitore invia quindi una richiesta all'erogatore, inserendo il voucher di PDND Interoperabilità nell'header autorizzativo Authorization
secondo il flusso standard. Nella stessa chiamata, inserisce in un secondo header chiamato Agid-JWT-TrackingEvidence
il JWS
con le informazioni complementari;
l'erogatore effettua le verifiche standard: l'erogatore estrae il voucher rilasciato da PDND Interoperabilità dall'header Authorization
ed effettua le sue verifiche, come da flusso standard;
l'erogatore effettua verifiche aggiuntive sul JWS: l'erogatore estrae il JWS
dall'header Agid-JWT-TrackingEvidence
. Verifica quindi sulle API di PDND Interoperabilità che la chiave pubblica corrispondente al kid
inserito nell'header del JWS
sia effettivamente depositata su PDND Interoperabilità e che il JWS
sia stato firmato con la chiave privata corrispondente;
l'erogatore calcola e confronta l'hash: l'erogatore effettua la stessa operazione effettuata dal fruitore al punto 2. e calcola l'hash del JWS
. Lo confronta quindi con quello inserito all'interno del voucher rilasciato da PDND Interoperabilità.
I due hash dovranno essere uguali, segno che quello che il fruitore ha dichiarato di aver trasmesso su PDND Interoperabilità è effettivamente ciò che l'erogatore (e lui solo) trova nelle informazioni complementari. Di seguito i passaggi nel dettaglio.
NB: è fatto divieto di inserire all'interno delle issue Github pubbliche le proprie client assertion o qualsiasi altro materiale di natura potenzialmente riservata o sensibile. Le issue che contengono informazioni potenzialmente sensibili potranno essere rimosse senza preavviso dagli amministratori.
Un JWS
di esempio può avere header
Il payload sarà invece costituito dalle informazioni aggiuntive che il fruitore vuole trasmettere all'erogatore. Il fruitore firma quindi il JWS
con una chiave privata. La chiave pubblica corrispondente deve essere depositata su PDND Interoperabilità e avere per kid
quello inserito nell'header del JWS
.
NB: questa chiave e questo kid
non devono necessariamente essere gli stessi con i quali si firma la client assertion al passaggio 3.
A partire dalla codifica del JWS
(ossia il JWS
codificato secondo l'algoritmo inserito nell'header, in genere inizia per ey
) il fruitore applica l'algoritmo di hashing SHA256
al JWS
, ottenendone un hash non reversibile a lunghezza fissa.
A scopo esemplificativo, è possibile inserire in un terminale il seguente comando, previa installazione del pacchetto openssl
per ottenere l'hash del JWS
. Ad esempio, a fronte del JWS esempio con codifica
si ottiene l'hash a lunghezza fissa
NB: la flag -n
che viene passata nel primo comando indica che vengano rimosse eventuali "newline" non viste dall'operatore. Un'eventuale "newline" presente nel token fa cambiare il valore dell'hash che poi non corrisponderà all'atto della verifica dell'erogatore.
Il fruitore prende quindi l'hash appena ottenuto, e lo inserisce nel payload della client assertion nel campo digest.value
. Il campo digest.alg
per adesso accetta solamente il valore SHA256
(corrispondente all'algoritmo di hashing che è stato applicato al JWS
).
L'header della client assertion rimane invece invariato rispetto al flusso standard. La client assertion andrà firmata con la chiave privata corrispondente alla pubblica caricata su PDND Interoperabilità il quale kid
è nell'header di questa asserzione.
In questo passaggio non ci sono variazioni rispetto al flusso standard. Il fruitore fa una richiesta di voucher al server autorizzativo di PDND Interoperabilità inviando la client assertion.
Anche in questo passaggio non ci sono variazioni. Da notare che l'unica verifica che PDND Interoperabilità effettua sul campo digest è che rispetti la lunghezza della stringa prevista da SHA256
(64 caratteri).
In questo passaggio, il fruitore costruisce una richiesta verso il servizio dell'erogatore secondo quanto descritto nel file di interfaccia e nella documentazione tecnica a corredo fornita dall'erogatore attraverso PDND Interoperabilità.
Nella richiesta inserirà l'header standard Authorization
che conterrà come Bearer
token il voucher rilasciato da PDND Interoperabilità allo step precedente. Inoltre, il fruitore inserirà il JWS
che contiene le informazioni complementari all'interno di un secondo header denominato Agid-JWT-TrackingEvidence
.
Dopo aver completato le verifiche standard, l'erogatore può effettuare le verifiche aggiuntive necessarie a garantire l'attendibilità delle informazioni complementari inserite nel JWS
aggiuntivo.
Quindi verificherà l'autenticità e la validità della chiave privata con la quale è firmato il JWS. Per farlo:
effettua una chiamata keys/{kid}
dove kid è valorizzato con il kid inserito nell'header del JWS
;
ottiene da PDND Interoperabilità una chiave pubblica in risposta all'interno del campo n
;
verifica la firma del JWS
, effettuata dal fruitore con la chiave privata, con la chiave pubblica appena ottenuta.
Se l'erogatore ottiene un errore con status code 404 - Not found
, significa che la chiave non è presente su PDND Interoperabilità e dunque la richiesta è da ritenersi inattendibile.
Se la chiave è presente e corrisponde, può procedere ad una seconda verifica, ossia quella notarile. In pratica, verifica che la traccia depositata su PDND Interoperabilità corrisponda a quella inserita all'interno del voucher rilasciato da PDND Interoperabilità.
Se c'è corrispondenza, vuol dire che le informazioni complementari inserite all'interno del JWS sono effettivamente quelle che il fruitore ha dichiarato su PDND Interoperabilità di aver inserito.
Per farlo, l'erogatore prende il JWS ed effettua la stessa operazione effettuata dal fruitore nel secondo passaggio. Ottiene quindi l'hash non reversibile del JWS.
L'erogatore confronta quindi questo hash con quello presente all'interno del campo digest.value
del voucher rilasciato da PDND Interoperabilità presente nell'header Authorization
. Se i due hash sono uguali, il fruitore ha reso una dichiarazione che corrisponde ed è tracciata su PDND Interoperabilità.
Il meccanismo descritto al passaggio 8 della guida nella sezione precedente ha un limite: per ogni verifica di voucher, sarebbe necessario ottenere la chiave pubblica corrispondente al kid
da PDND Interoperabilità, con evidenti ripercussioni sulle performance e sostenibilità del sistema.
Per ovviare, suggeriamo agli erogatori che sfruttano questa funzionalità di dotarsi di una cache nella quale conservare una copia delle chiavi pubbliche e dei relativi kid
.
Per supportare gli erogatori in quest'attività, PDND Interoperabilità mette a disposizione un secondo endpoint sulle proprie API:/events/keys
.
Ogni risultato ha la seguente struttura:
NB: L'endpoint deve essere chiamato dall'aderente in modalità polling.
Utilizzando l'endpoint /events/keys
è possibile chiamare puntualmente l'endpoint /keys/{kid}
per recuperare tutte le chiavi nuove che vengono aggiunte (quelle con eventType == "ADDED"
).
Quelle chiavi per le quali l'evento è DELETED
vanno rimosse dalla propria cache. Se si vuole ottenere una controprova, anche qui è possibile chiamare l'endpoint /keys/{kid}
con il kid
della chiave che si è riscontrato essere stata cancellata, e il sistema restituirà uno status code 404 - Not Found
.
La richiesta e il dettaglio implementativo delle informazioni aggiuntive dovranno essere esplicitate dall'erogatore all'interno della documentazione tecnica a corredo dell'e-service.
È possibile per gli erogatori mettere a disposizione dei fruitori un ulteriore livello di sicurezza, che garantisca l'integrità della risposta fornita.
In sostanza, gli erogatori firmano la propria risposta con una chiave privata, la cui corrispondente chiave pubblica è depositata su PDND Interoperabilità per le dovute verifiche.
l'erogatore deve avere creato un portachiavi;
deve averlo associato ad un e-service attraverso la tab "Portachiavi" disponibile all'interno della scheda e-service (Erogazione > I tuoi e-service);
un suo operatore di sicurezza o amministratore deve aver caricato almeno una chiave pubblica all'interno di quel portachiavi.
Sarà quindi possibile per l'erogatore firmare la propria risposta con la propria chiave privata. Il fruitore potrà verificare la corrispondente chiave pubblica depositata su PDND Interoperabilità.
Di seguito riportiamo a titolo di esempio una possibile gestione del meccanismo di firma del payload di risposta di un e-service.
Questa sezione descrive come firmare un payload di risposta HTTP utilizzando RSA, per garantire che i dati provenienti da un e-service e non siano stati modificati.
La risposta JSON che l'erogatore invia al fruitore sarà strutturata come segue:
dove
data: contiene il payload, ossia i dati effettivi che l'e-service trasmette verso i fruitori;
signature: contiene la firma digitale del campo data, calcolata dall'e-service utilizzando una chiave privata RSA (appartenente ad un portachiavi all'e-service) e codificata in formato base64;
kid: identificatore della chiave usata per la firma; consente al fruitore di sapere quale chiave pubblica utilizzare per verificare la firma.
Il contenuto del campo data viene convertito in una stringa di byte e sottoposto a una funzione di hash utilizzando un algoritmo come SHA-256.
L’hash calcolato è poi firmato utilizzando una delle chiavi RSA, identificata univocamente tramite kid, appartenente ad un portachiavi associato all'e-service. La firma garantisce che solo chi possiede la chiave privata corrispondente a kid (erogatore) possa generare la firma specifica per quel contenuto. Il kid della chiave pubblica che si è caricata è disponibile all'interno del portachiavi, aprendo la pagina relativa alla singola chiave (Erogazione > I tuoi portachiavi, clicchi sul portachiavi di interesse, tab Chiavi pubbliche, e clicchi sulla chiave di tuo interesse).
La firma è codificata in base64 e inclusa nel payload JSON nel campo signature, mentre kid identifica univocamente la chiave usata, per consentire al fruitore di selezionare la chiave pubblica corretta per la verifica.
Il fruitore può verificare l’autenticità e l’integrità dei dati ricevuti nella risposta utilizzando la chiave pubblica RSA associata all'e-service. All'interno della risposta troverà il campo kid, che identifica la chiave pubblica corrispondente alla privata che l'erogatore ha utilizzato per firmare la risposta.
Il fruitore calcola l’hash del contenuto di data usando lo stesso algoritmo utilizzato dell'erogatore (SHA-256).
Con la chiave pubblica individuata tramite kid, il fruitore verifica che la firma (campo signature) sia valida rispetto all’hash calcolato. Se corrispondono, il payload è autentico e integro; in caso contrario, potrebbe essere stato alterato o non provenire dall'e-service.
Il processo di firma e verifica segue specifiche internazionali che garantiscono la sicurezza. Gli standard di riferimento includono:
Il pre-requisito per poter ottenere un voucher valido è aver caricato almeno una chiave pubblica, parte del proprio materiale crittografico, all'interno di un client api interop (disponibile sull'interfaccia del back office alla voce Fruizione > I tuoi client api interop). Per saperne di più, leggi la .
iat
: l'issued at, il timestamp riportante data e ora in cui viene creato il token, espresso in (valore numerico, non stringa);
exp
: l'expiration, il timestamp riportante data e ora di scadenza del token, espresso in (valore numerico, non stringa).
La prima è che il pre-requisito per poter ottenere un voucher valido è aver caricato almeno una chiave pubblica all'interno di un client e-service associato ad una finalità attiva, invece di un client api interop. Anche qui, si veda la .
L'erogatore scarica la lista di chiavi in uso da un file esposto nella cartella .well-known
di PDND Interoperabilità (l'URL corretta è disponibile sull'interfaccia nel back office all'interno della scheda di ogni singolo e-service e cambia in funzione dell'ambiente; a titolo esemplificativo, è quella di produzione).
Per maggiori informazioni rimandiamo alla .
Dal punto di vista tecnico, l'interazione si articola in alcuni passaggi, che sono di fatto una variazione sul tema rispetto al flusso standard di richiesta e verifica di un voucher, descritto . In particolare:
il fruitore impacchetta le informazioni complementari: il fruitore costruisce un JWS
secondo la specifica definita nell' inserendo nell'header il kid
di una chiave pubblica depositata su PDND Interoperabilità. Con la chiave privata corrispondente a quella pubblica firmerà questo JWS
. Nel corpo (payload) del JWS
inserisce le informazioni complementari da inviare all'erogatore;
Per questo passaggio, si veda la nel flusso standard.
si autentica sulle API di Interoperabilità come descritto nel ;
Gli eventi sulle chiavi hanno lo stesso meccanismo di funzionamento di quello degli altri eventi, descritto nella . In sostanza, è possibile chiamare l'endpoint /events/keys
passando come parametri lastEventId
(richiesto) e limit
(opzionale, default a 100). Il primo indica l'id dell'ultimo evento scaricato, il secondo quanti eventi si intende scaricare.
Il meccanismo di caricamento e gestione delle chiavi è analogo a quello dei client, ed è disponibile nella sezione Erogazione > I tuoi portachiavi. Si rimanda alla per tutte le operazioni legate alla gestione di un portachiavi.
La chiave corrispondente al kid è disponibile sulle al path GET /keys/{kid}
, dal quale otterrà la chiave pubblica corrispondente al kid in formato JWK.
(PKCS #1): definisce le modalità di utilizzo dell’algoritmo RSA per la firma digitale;
(JSON Web Algorithms): specifica gli algoritmi di firma, come RSA e SHA-256;
(JSON Web Key — JWK): questo documento specifica il formato JSON per rappresentare le chiavi crittografiche, sia pubbliche che private.