# Aurelia Autenticazione e licensing

## legenda
In questa rappresentazione di un nodo Aurelia abbiamo:
- *Notary*, il servizio in grado di autenticare gli utenti e di gestirne l'anagrafica
- *Watchman*, il servizio che autorizza tutte le richieste provenienti dai client esterni
- Client di vario tipo, i blocchi azzurri, che accedano dall'esterno del nodo
- *Hydra*, il servizio che implementa il protocollo OpenID Connect
## Autenticazione
Il protocollo di autorizzazione scelto è OAuth2 (OpenID Connect si basa su quest'ultimo e aggiunge il concetto di *identità dell'utente*).
Per implementare il protocollo OAuth2 abbiamo scelto un server open-source già pronto [Hydra](https://www.ory.sh/hydra/) che disaccoppia l'implementazione del protocollo stesso dal flusso di autenticazione che rimane a carico di un software scritto da noi (Notary).
Dei vari flussi di autenticazione supportati da OAuth2 implementeremo *Authorization Code Grant with PKCE*. L'Authorization Code Grant è il flusso più sicuro implementato da OAuth2 e permette di ottenere anche i refresh token utili per permettere ad un applicazione di ottenere un nuovo token senza l'intervento dell'utente.
L'estensione PKCE permette di utilizzare in maniera sicura l'Authorization Code Grant anche quando l'applicazione è installata in un ambiente fuori dal nostro controllo (come i programmi desktop e le web app).
L'autorizzazione viene eseguita da ogni client parlando con il server Hydra (le linee viola nel diagramma) e al termine i client ottengono un token da includere in ogni richiesta verso il nodo Aurelia.
### Autenticazione interattiva
La classica autenticazione basata su browser dove l'utente è chiamato a verificare la propria identità inserendo qualcosa (potrebbe essere username + password, barcode + sede + m2fa, etc). La pagina HTML viene generata da *Notary* ed è personalizzata per il singolo cliente.
### Autenticazione interattiva SSO
TODO da verificare
Un autenticazione che sfrutta il SSO di windows. L'idea è che la pagina di login su *Notary* effettui un redirect sull'Active Directory (ad esempio) del cliente che, grazie all'autenticazione di dominio inviata dai browser, è in grado di autenticare l'utente senza chiedergli nulla (ad eccezione della prima volta dove deve autorizzare Aurelia). Una volta autenticato l'utente viene rediretto su *Notary* che recupera le informazioni sull'utente dal server SSO e completa il login.
### Autenticazione non interattiva
Un autenticazione *non interattiva* pensata per i client desktop (come Caligola o il GL) per quei casi dove:
- non è detto che il cliente abbia necessità di distinguere tra i vari utenti che usano il software
- non si possa/voglia usare il flusso di login basato sui browser
L'idea è di salvare localmente (su disco o nel keychain) un token generato da Aurelia e collegato con una certa utenza (ogni utente può avere più token) censita da *Notary*.
Il client inizia la sequenza di login e intercetta il redirect dal server hydra (dove c'è l'endpoint OAuth2) al server *Notary* che deve mostrare la UI di login; prima di seguire il redirect aggiunge un header HTTP con il token utente.
Se il token è valido *Notary* non mostrerà la UI di login ma completerà immediatamente il login.
## Licensing
Una licenza d'uso da la possibilità ad un utente **autorizzato** di utilizzare un determinato software. Il conteggio ed il controllo delle licenze è un problema ortogonale all'autorizzazione ma che può essere risolto sfruttando le garanzie offerte da quest'ultima.
In ambito cloud possiamo parlare di API invece che di software quindi, in prima istanza, potremmo dire che per chiamare una qualunque API offerta da Aurelia è necessario disporre di una licenza valida.
Purtroppo questo modello, sebbene corretto, non è sufficiente a soddisfare tutti i requisiti:
- alcuni software devono poter funzionare, e quindi per definizione avere una licenza valida, anche se sono offline;
- alcuni software sono venduti con una licenza di tipo *floating*, che viene conteggiata solo quando il software è in uso.
Quando questi due requisiti sono richiesti dallo stesso software le cose si complicano; stiamo parlando di un programma che deve avere una licenza offline per poter essere eseguito anche quando la connessione ad internet manca ma che non deve essere conteggiata quando il software non viene usato.
Ricapitolando:
- Ogni richiesta API deve essere autenticata.
- Ogni richiesta API richiede una licenza.
- Ogni software, per poter funzionare, deve avere una licenza.
- Alcuni software devono poter funzionare anche scollegati ad internet (ma va bene essere online per ottenere la licenza).
> [name=Matteo Bertini] nel senso che almeno la prima volta devono essere online?
- Alcuni software conteggiano la propria licenza d'uso sui minuti di effettivo utilizzo.
### Sessioni utente, licenze e token OAuth2
Introduciamo il concetto di sessione utente; una sessione utente viene allocata durante il login se:
- le credenziali di login sono corrette
- esiste una licenza disponibile
Nota: Per il momento, dati i vari casi d'uso per le licenze (vedi "Durata della licenza"), il controllo sulla disponibilità di una licenza non è necessario.
Aggiungiamo al token OAuth2, restituito al termine dell'autorizzazione, due campi: `session_id` e `license`.
Il primo (`session_id`) è l'identificativo della sessione utente generato dal servizio di login.
Il secondo (`license`) è un JWT con i dettagli della licenza in uso.
Tra i dettagli (claims) della licenza c'è:
- la durata della licenza (claim *exp*)
- l'hardware per il quale è stata rilasciata (claim *hw_fingerprint*)
La durata della licenza **non è collegata** alla durata del token OAuth2.
Con queste modifiche abbiamo collegato ogni richiesta ad una sessione utente (e ad una licenza) e comunicato al client per quanto tempo può continuare a funzionare prima di aver bisogno di una nuova licenza.
### Durata della licenza
La durata di una licenza dipende dal tipo del client e dai dettagli dell'utente (come la sede) che la richiede.
| tipo | durata di default | connettività |
| --------- | ----------------- | -------------- |
| hw | unlimited | inaffidabile |
| desktop | 1 gg | inaffidabile |
| web | 10 minuti | affidabile |
| 3rd party | unlimited | non importante |
Un client di tipo *hw* (come il GL) può avere una connessione ad internet non affidabile ma è venduto insieme ad una macchina industriale; non ha senso limitare artificialmente la durata della licenza software. Potrebbe aver senso invece, un domani, istituire un'anagrafica delle macchine corredata dai loro numeri di serie (o concetto simile) e impedire il login alle macchine non registrate.
> [name=Matteo Bertini] in questo caso quindi quando viene associato il token, al primo avvio o prima di spedire la macchina?
Un client di tipo *desktop* (come Caligola) può avere una connessione ad internet non affidabile ma deve poter essere utilizzato anche in assenza di connessione. Il client, a cui viene rilasciata una licenza di lunga durata, verifica in autonomia fino a quando può restare in esecuzione. La licenza può anche essere salvata su disco ed essere utilizzata tra avvii diversi.
Un client di tipo *web* ha, per definizione, una connessione affidabile e per questo possiamo fornirgli una licenza con una durata *breve*; possiamo ragionevolmente presumere che sia possibile implementare un meccanismo in background con cui la licenza viene periodicamente *estesa*.
Sono due i motivi per cui vogliamo ridurre la durata della licenza ed hanno entrambi a che vedere con il conteggio delle stesse:
1. L'ambiente in cui vengono eseguite le applicazioni web (il browser) permette a qualsiasi utente di manipolare facilmente le applicazioni in esecuzione, per questo non possiamo fidarci e permettere a queste applicazioni di fare il reshaping degli utilizzi (vedi Conteggio delle licenze)
2. Senza la possibilità di fare reshaping la licenza verrebbe conteggiata come in uso dal momento in cui l'utente fa login a quando fa logout (vedi Rilascio della licenza); ma il fare logout è un'operazione che pochissimi utenti eseguono normalmente, anzi il modo standard di utilizzare una qualsiasi applicazione web moderna è di rimanere sempre connessi.
Entrambi questi problemi possono essere mitigati da una licenza d'uso con una durata breve.
> [name=Matteo Bertini] nel senso che l'app identifica l'utente come idle e non rinnova la licenza?
Un client *3rd party* è sviluppato da terzi (quindi per definizione fuori dal nostro controllo) e può svolgere compiti di qualsiasi tipo (integrazione, creazione contenuti, statistiche). Quale che sia il suo compito probabilmente non ci guadagniamo nulla a limitare la licenza d'uso che gli rilasciamo, potrebbe essere più opportuno applicare altri tipo di limitazioni (ad esempio il numero di richieste in un dato tempo).
> [name=Matteo Bertini] anche per questi, come ci immaginiamo che vengano attivati? Tipo Foobar prende Aurelia, poi vuole connetterla al suo PLM. Dove si crea e dove si monitora il token che gli assegnamo?
### Rilascio della licenza
Una licenza viene considerata in uso finché non scade o fino a quando la sessione utente non viene terminata facendo logout.
> [name=Matteo Bertini] oppure immagino chiudendo la finestra del browser ad una mancata richiesta per rinfrescare le credenziali il server lo considera logout.
### Estensione della licenza
Una licenza può essere estesa chiamando una API che, se possibile, estenderà la durata della licenza collegata con la sessione utente.
### Conteggio della licenza
Ad oggi prevediamo due tipi di licenza:
1. licenze *upfront*
1. licenze *floating*
Le licenze di tipo *upfront* sono quelle, ad oggi, con una durata *unlimited* mentre le altre sono *floating*.
Il conteggio di una licenza, per fini commerciali, ha senso solo per le licenze *floating*.
L'idea di base è che una licenza si intende in uso dal momento in cui viene emessa fino a quando scade o non viene rilasciata.
Il problema sorge con i client come Caligola che hanno bisogno di una licenza con durata lunghissima per poter lavorare offline, ma che devono poter conteggiare il tempo effettivo di utilizzo (ad esempio per non considerare la licenza in uso durante la notte).
Per risolvere questo problema ad alcuni client sarà permesso di fare *usage reshaping*, tramite un API potranno comunicare gli intervalli di tempo in cui sono stati effettivamente in uso.
> [name=Matteo Bertini] il modellista Caio apre il cad, fa login, e comincia a lavorare. La sua connessione viene isolata, e lui lavora per 3h, poi chiude e va a pranzo. Il mattino successivo c'è di nuovo linea, il cad si sincronizza con Aurelia, prende un token "fresco" e manda il reshaping, dicendo: di tutto il tempo che non ci siamo visti Caio ha lavorato dalle 9:00 alle 12:00.
> 1. Cosa succede se per qualche ragione Caio disinstalla il Cad prima del reshaping?
> 2. Cosa succede se accede ad internet via VPN e l'orologio di sistema va a spasso? (qua si spera che sappia anche in che time zone è... dipenderà dal sistema operativo, ma spero lo facciano tutti)
> 3. Cosa succede se Caio si salva il profilo del cad a sessione aperta e lo usa su più macchine? (qua ci dovrebbe pensare `hw_fingerprint`, ma alle macchine virtuali è facile dare lo stesso mac address)
> 4. Cosa succede se Caio riesce a cambiare il log dell'uso, o se decidiamo di firmarlo, a forgiarlo recuperando la chiave di calgola da qualche parte?
>
> Per 4) dicevamo al volo in call che:
> - potrebbe essere una strategia commerciale vendere 5 sessioni di Caligola full time, estendibili fino a 20 (5 si pagano sempre, è il minimo uso previsto),
> - a posteriori, sarebbe possibile fare anomaly detection sui reshape, e magari investigare sui casi atipici.
### Controllo della licenza
Il controllo della licenza viene effettuato dal servizio *Watchmen* ad ogni API grazie al token OAuth2 dal quale viene recuperata la licenza collegata.
### Validità di una licenza
Una licenza deve essere validata localmente da ogni client.
Una licenza è valida se:
1. è stata emessa da Aurelia (verificata tramite chiave pubblica)
1. non è scaduta
1. è stata emessa per l'hw su cui è in esecuzione il client.
### Formato di una licenza
Una licenza è un documento JWT firmato con una chiave pubblica/privata.
I claim presenti nel token sono:
- exp; (claim standard) la scandenza della licenza
- iss; (claim standard) la url del nodo Aurelia che ha emesso il token
- license_id; (claim custom) un id univoco e causale per identificare questa licenza
- hw_fingerprint; (claim custom) un valore, generato dal client, che identificare uno specifico computer, l'unico che può utilizzare questa licenza.
## Caso d'uso: GL
- client: desktop
- licenza: upfront
- offline: si
Appena ne ha bisogno il GL fa login su Aurelia (magari utilizzando il login non interattivo basato su token) e utilizza le sue API senza preoccuparsi di nessun aspetto relativo al licensing.
## Caso d'uso: Caligola
- client: desktop
- licenza: floating
- offline: si
All'avvio Caligola controlla se c'è una licenza valida salvata localmente.
Se la licenza esiste Caligola può avviarsi, altrimenti prova a fare login su Aurelia (magari utilizzando il login non interattivo basato su token) per ottenerne una e se il login fallisce termina.
> [name=Matteo Bertini] non ho capito bene come funziona il token, ma immagino sia astratto a livello di libreria. Ma se ci fossero delle invarianti da rispettare anche a livello di applicazione, magari possiamo anticiparlo qua.
Una volta avviato Caligola fa partire un thread in background che ogni 5 minuti prova a contattare Aurelia per estendere la licenza in uso e per fare *usage reshaphing*; un eventuale fallimento (ad esempio perché offline) non diventa un problema fin quando la licenza in uso è valida.
> [name=Matteo Bertini] in caso di *usage reshaphing* fallito, Caligola (o la libreriea che esporrà l'API) si salva il log di uso da qualche parte, meglio se firmato o almeno offuscato. L'applicazione può contere una chiave privata, ma in qualche modo è sempre accessibile. Stessa cosa c'è per la chiave con cui è crittato il pacchetto PyInstaller, non sappiamo se qualcuno si è messo a estrarla per modificare il codice. Essendo le due cose di complessità analoga possiamo supporre che chi accede alla chiave privata può a questo punto evitare in toto il controllo della licenza con poco più sforzo.
> Però dovremmo essere sicuri almeno che chi rompe la licenza non possa usare a sbafo anche le API.
Ogni volta che Caligola riesce ad estendere la licenza la salva localmente.
Se la licenza in uso scade e non è possibile estenderla Caligola termina.
## Caso d'uso: WebApp
- client: web
- licenza: floating
- offline: no
All'avvio il client web fa login su Aurelia (e grazie ai cookie settati da Hydra non vede ogni volta la pagina di login), ottiene una licenza e fa partire un worker thread che ogni cinque minuti prova ad estenderla.
## Caso d'uso: 3rd Party
- client: 3rd party
- licenza: upfront
Appena ne ha bisogno il servizio 3rd Party fa login su Aurelia e utilizza le sue API senza preoccuparsi di nessun aspetto relativo al licensing.
> [name=Matteo Bertini] non doveva avere un token? Vedi domanda sopra di quando lo assegnamo questo token?
## Risorse anonime
TODO: Ha senso avere delle API anonime? Ad esempio l'API che genera l'svg di un pezzo può essere memorizzata in un PLM e usata in un secondo momento (magari a distanza di anni)? Meglio avere delle API anonime o aggiungere il supporto per le url *firmate*?
> [name=Matteo Bertini] il PLM non dovrebbe già avere accesso via token illimitato? Serve una seconda via?
> In pratica quel token è quasi per cliente, a loro la cura di non lasciarlo andare a giro perché è autenticato e concordo sul rate-limited. Poi possono usarlo sul PLM o sul backend del sito, a noi interessa il giusto, no?