OpenID Connect: autorizzazione di applicazioni interne da soluzioni personalizzate a standard

Qualche mese fa, mi sono dedicato all'implementazione di un server OpenID Connect per la gestione dell'accesso a centinaia delle nostre applicazioni interne. Siamo passati dalle nostre soluzioni personalizzate, adatte a scale più piccole, a uno standard accettato a livello generale. L'accesso tramite un servizio centrale semplifica notevolmente operazioni monotone, riduce i costi di implementazione delle autorizzazioni, consente di trovare molte soluzioni pronte all'uso e di evitare complicazioni durante lo sviluppo di nuove soluzioni. In questo articolo parlerò di questo passaggio e delle difficoltà che abbiamo affrontato.

OpenID Connect: autorizzazione di applicazioni interne da soluzioni personalizzate a standard

Tanto tempo fa… Come tutto è cominciato

Qualche anno fa, quando le applicazioni interne erano diventate troppo numerose per la gestione manuale, abbiamo sviluppato un'applicazione per il controllo degli accessi all'interno dell'azienda. Era una semplice applicazione Rails che si collegava a un database contenente informazioni sui dipendenti, dove veniva configurato l'accesso a diverse funzionalità. In quel momento abbiamo anche avviato il nostro primo SSO, basato sulla verifica dei token tra il client e il server di autorizzazione; il token veniva trasmesso in forma crittografata con vari parametri e convalidato dal server di autorizzazione. Non era la soluzione più comoda, poiché ogni applicazione interna richiedeva l'aggiunta di un notevole livello di logica, e i database dei dipendenti dovevano essere sincronizzati con il server di autorizzazione.

Dopo un po' di tempo, abbiamo deciso di semplificare la questione dell'autenticazione centralizzata. Abbiamo spostato l'SSO su un bilanciatore. Con OpenResty su Lua, abbiamo aggiunto un modello che verificava i token, sapeva a quale applicazione era destinata la richiesta e poteva controllare se vi era accesso. Questo approccio ha semplificato notevolmente il controllo degli accessi alle applicazioni interne: non era più necessario descrivere logica aggiuntiva nel codice di ogni applicazione. Di conseguenza, abbiamo chiuso il traffico esterno, e l'applicazione stessa non sapeva nulla dell'autenticazione.

Tuttavia, uno dei problemi è rimasto irrisolto. Cosa fare con le applicazioni che necessitano di informazioni sui dipendenti? Si potrebbe sviluppare un'API per il servizio di autorizzazione, ma sarebbe stata necessaria l'aggiunta di logica supplementare per ciascuna di queste applicazioni. Inoltre, volevamo liberarci della dipendenza da una nostra applicazione personalizzata, mirata in futuro a diventare OpenSource, dal nostro server di autorizzazione interno. Di questo ne parleremo un'altra volta. La soluzione a entrambi i problemi è stata OAuth.

Verso standard accettati a livello generale

OAuth è uno standard di autorizzazione chiaro e accettato a livello generale, ma poiché le sue funzionalità non erano sufficienti, abbiamo iniziato a considerare anche OpenID Connect (OIDC). OIDC, in quanto tale, è la terza implementazione di uno standard di autenticazione aperto, fondata sopra il protocollo OAuth 2.0 (protocollo di autorizzazione aperto). Questa soluzione affronta il problema della mancanza di dati sugli utenti finali e offre anche la possibilità di cambiare fornitore di autorizzazione.

Tuttavia, non abbiamo scelto un fornitore specifico e abbiamo deciso di integrare OIDC nel nostro server di autorizzazione esistente. A favore di questa soluzione ha giovato il fatto che OIDC è molto flessibile per quanto riguarda l'autenticazione dell'utente finale. Così abbiamo avuto l'opportunità di implementare il supporto OIDC sul nostro server di autorizzazione attuale.

OpenID Connect: autorizzazione di applicazioni interne da soluzioni personalizzate a standard

Il nostro percorso per implementare il server OIDC

1) Abbiamo formattato i dati nel modo giusto

Per integrare OIDC è necessario formattare i dati attuali sugli utenti in un formato comprensibile allo standard. In OIDC, questo è chiamato Claims. I Claims, in sostanza, sono i campi finali nel database sugli utenti (nome, email, telefono, ecc.). Esiste un elenco standard di claims, e tutto ciò che non rientra in questo elenco è considerato personalizzato. Pertanto, il primo punto a cui prestare attenzione, se si desidera scegliere un fornitore OIDC esistente, è la possibilità di una personalizzazione comoda dei nuovi claims.

Un gruppo di claims è raggruppato nel seguente sottoinsieme – Scope. Durante l'autenticazione viene richiesta l'autorizzazione non a claims specifici, ma a scope, anche se alcuni claims all'interno dello scope non sono necessari.

2) Abbiamo implementato i grant necessari

La parte successiva dell'integrazione OIDC è la selezione e l'implementazione dei tipi di autorizzazione, noti come grant. Il grant scelto influirà sul successivo scenario di interazione dell'applicazione selezionata con il server di autorizzazione. Uno schema esemplificativo della scelta del grant necessario è riportato nell'immagine qui sotto.

OpenID Connect: autorizzazione di applicazioni interne da soluzioni personalizzate a standard

Per la nostra prima applicazione, abbiamo utilizzato il grant più comune: Authorization Code. La sua distinzione rispetto agli altri sta nel fatto che è un processo in tre fasi, ossia prevede un controllo aggiuntivo. Prima l'utente fa una richiesta di autorizzazione, riceve un token – Authorization Code, e poi, utilizzando questo token come un biglietto, richiede un token di accesso. Tutto il principale interscambio in questo scenario di autorizzazione si basa su redirect tra l'applicazione e il server di autorizzazione. Maggiori dettagli su questo grant possono essere letti qui.

OAuth aderisce al concetto che i token di accesso ottenuti dopo l'autenticazione devono essere temporanei e dovrebbero ideally cambiare mediamente ogni 10 minuti. Il grant Authorization Code è un controllo in tre passaggi tramite redirect, e ogni 10 minuti fare tale passaggio non è, a dirla tutta, la cosa più piacevole da vedere. Per risolvere questo problema, esiste un altro grant: il Refresh Token, che abbiamo implementato anche noi. Qui la situazione è più semplice. Durante un controllo da un altro grant, oltre al principale token di accesso viene fornito anche un altro – Refresh Token, che può essere utilizzato solo una volta e il cui tempo di vita, di solito, è notevolmente più lungo. Con questo Refresh Token, quando scade il TTL (Time to Live) del principale token di accesso, la richiesta di un nuovo token di accesso arriva a un endpoint di un altro grant. Il Refresh Token utilizzato viene immediatamente annullato. Tale verifica è un processo in due passaggi e può essere eseguita in background, senza che l'utente se ne accorga.

3) Abbiamo configurato i formati di output dei dati utente

Dopo che i grant selezionati sono stati implementati e l'autenticazione funziona, è opportuno menzionare la raccolta dei dati sull'utente finale. In OIDC esiste un endpoint separato per questo, dove con il proprio attuale token di accesso e se è ancora valido, è possibile richiedere i dati degli utenti. E se i dati dell'utente non cambiano così frequentemente, ma è necessario accedere ai dati attuali molte volte, si può arrivare a una soluzione come i token JWT. Questi token sono anch'essi supportati dal standard. Di per sé, un token JWT è composto da tre parti: header (informazioni sul token), payload (qualunque dato necessario) e signature (firma, il token è firmato dal server e in seguito è possibile verificare la fonte della sua firma).

Nell'implementazione di OIDC, il token JWT è chiamato id_token. Può essere richiesto insieme al normale token di accesso e tutto ciò che resta è verificare la firma. Il server di autorizzazione ha per questo un endpoint separato con una serie di chiavi pubbliche in formato JWK. E parlando di questo, vale la pena menzionare che esiste un altro endpoint che, in base allo standard RFC5785 , riflette la configurazione attuale del server OIDC. Contiene tutti gli indirizzi degli endpoint (incluso l'indirizzo della serie di chiavi pubbliche utilizzate per la firma), le claim supportate e gli scope, gli algoritmi di crittografia supportati, i grant disponibili, ecc.

Ad esempio, in Google:

{
 "issuer": "https://accounts.google.com",
 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
 "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
 "token_endpoint": "https://oauth2.googleapis.com/token",
 "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
 "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
 "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
 "response_types_supported": [
  "code",
  "token",
  "id_token",
  "code token",
  "code id_token",
  "token id_token",
  "code token id_token",
  "none"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "RS256"
 ],
 "scopes_supported": [
  "openid",
  "email",
  "profile"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_post",
  "client_secret_basic"
 ],
 "claims_supported": [
  "aud",
  "email",
  "email_verified",
  "exp",
  "family_name",
  "given_name",
  "iat",
  "iss",
  "locale",
  "name",
  "picture",
  "sub"
 ],
 "code_challenge_methods_supported": [
  "plain",
  "S256"
 ],
 "grant_types_supported": [
  "authorization_code",
  "refresh_token",
  "urn:ietf:params:oauth:grant-type:device_code",
  "urn:ietf:params:oauth:grant-type:jwt-bearer"
 ]
}

Così, tramite l'id_token, è possibile trasmettere tutte le claim necessarie nel payload del token e non dover richiedere ogni volta al server di autorizzazione i dati sull'utente. Un aspetto negativo di questo approccio è che la modifica dei dati dell'utente dal server non avviene immediatamente, ma insieme a un nuovo token di accesso.

Risultati dell'implementazione

Dopo aver implementato il nostro server OIDC e configurato le connessioni ad esso dal lato delle applicazioni, abbiamo risolto il problema della trasmissione delle informazioni sugli utenti.
Poiché OIDC è uno standard aperto, abbiamo avuto la possibilità di scegliere un provider esistente o implementare un server. Abbiamo provato Keycloak, che si è dimostrato molto comodo da configurare; dopo la configurazione e il cambiamento delle impostazioni di connessione dal lato delle applicazioni, è pronto per il lavoro. Dal lato delle applicazioni, rimane solo da cambiare le configurazioni di connessione.

Parlando di soluzioni esistenti

Nell'ambito della nostra organizzazione, abbiamo sviluppato la nostra implementazione come primo server OIDC, ampliandola secondo necessità. Dopo un'attenta valutazione di altre soluzioni pronte all'uso, si può affermare che questo è un punto controverso. A favore della decisione di realizzare un proprio server ci sono state preoccupazioni da parte dei fornitori riguardo all'assenza delle funzionalità necessarie, oltre alla presenza di un vecchio sistema con varie autorizzazioni personalizzate per alcuni servizi e già molti dati sui dipendenti. Tuttavia, nelle soluzioni pronte ci sono comodità per l'integrazione. Ad esempio, in Keycloak c'è un proprio sistema di gestione degli utenti e i dati sono memorizzati direttamente lì, e trasferire i propri utenti non è difficile. A tal fine, Keycloak dispone di un'API che consente di eseguire tutte le operazioni necessarie per il trasferimento.

Un altro esempio interessante e certificato, a mio avviso, è Ory Hydra. È interessante perché è composto da diversi componenti. Per l'integrazione, sarà necessario collegare il tuo servizio di gestione utenti con il loro servizio di autorizzazione e ampliare secondo necessità.

Keycloak e Ory Hydra non sono le uniche soluzioni pronte. È meglio scegliere un'implementazione certificata dalla OpenID Foundation. Di solito, queste soluzioni hanno il simbolo della certificazione OpenID.

OpenID Connect: autorizzazione di applicazioni interne da soluzioni personalizzate a standard

Non dimenticate nemmeno i fornitori a pagamento esistenti, se non volete mantenere il vostro server OIDC. Oggi ci sono molte buone opzioni.

Cosa succede dopo

Nel prossimo futuro, pianifichiamo di chiudere il traffico verso i servizi interni in un altro modo. Abbiamo in programma di trasferire il nostro attuale SSO su un bilanciatore tramite OpenResty su un proxy, basato su OAuth. Anche qui ci sono già molte soluzioni pronte, ad esempio:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Materiale aggiuntivo

jwt.io – un buon servizio per la verifica dei token JWT
openid.net/developers/certified — elenco delle implementazioni OIDC certificate

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster