OpenID Connect: autorizarea aplicațiilor interne de la soluții personalizate la standard

Câteva luni în urmă, m-am ocupat de implementarea unui server OpenID Connect pentru gestionarea accesului la sute dintre aplicațiile noastre interne. De la soluțiile noastre proprii, utile la o scară mai mică, am trecut la un standard recunoscut. Accesul printr-un serviciu centralizat simplifică semnificativ operațiunile monotone, reduce costurile pentru implementarea autorizațiilor, permite găsirea multor soluții gata făcute și elimină stresul în dezvoltarea de noi soluții. În acest articol, voi vorbi despre această tranziție și despre dificultățile pe care le-am întâmpinat.

OpenID Connect: autorizarea aplicațiilor interne de la soluții personalizate la standard

Cu mult timp în urmă… De unde a început totul

Cu câțiva ani în urmă, când aplicațiile interne au devenit prea multe pentru a fi gestionate manual, am scris o aplicație pentru controlul accesului în cadrul companiei. A fost o aplicație simplă în Rails, care se conecta la o bază de date cu informații despre angajați, unde se configura accesul la diverse funcționalități. Atunci am implementat primul SSO, care se baza pe verificarea token-urilor din partea clientului și a serverului de autorizare, token-ul fiind transmis într-o formă criptată cu mai mulți parametri și verificat pe serverul de autorizare. Aceasta nu era cea mai convenabilă variantă, deoarece pentru fiecare aplicație internă era necesară descrierea unui strat considerabil de logică, iar bazele de date cu angajați nu erau sincronizate cu serverul de autorizare.

După un timp, am decis să simplificăm sarcina autorizării centralizate. Am mutat SSO-ul pe un load balancer. Cu ajutorul OpenResty pe Lua, am adăugat un șablon care verifica token-urile, știa spre ce aplicație se îndreaptă cererea și putea verifica dacă există acces. Această abordare a simplificat semnificativ sarcina controlului accesului la aplicațiile interne — în codul fiecărei aplicații nu mai era necesară descrierea unei logici suplimentare. În cele din urmă, am închis traficul extern, iar aplicația însăși nu știa nimic despre autorizare.

Însă, una dintre probleme a rămas nerezolvată. Ce facem cu aplicațiile care au nevoie de informații despre angajați? S-ar fi putut scrie un API pentru serviciul de autentificare, dar atunci ar fi fost necesar să adăugăm o logică suplimentară pentru fiecare astfel de aplicație. În plus, ne-am dorit să scăpăm de dependența de o aplicație pe care am dezvoltat-o noi înșine, orientată spre tranziția către OpenSource, de serverul nostru intern de autentificare. Despre acesta vom povesti altădată. Soluția ambelor probleme a fost OAuth.

Standarde acceptate pe scară largă

OAuth este un standard de autorizare bine cunoscut și acceptat, dar deoarece funcționalitatea sa nu este suficientă, s-a început analiza imediată a OpenID Connect (OIDC). OIDC, în sine, reprezintă a treia implementare a standardului deschis de autentificare, care a evoluat ca o extensie a protocolului OAuth 2.0 (protocol deschis de autorizare). Această soluție rezolvă problema absenței informațiilor despre utilizatorul final, precum și oferă posibilitatea schimbării furnizorului de autorizare.

Cu toate acestea, nu am ales un furnizor specific și am decis să adăugăm integrarea cu OIDC pe serverul nostru de autentificare existent. Decizia a fost influențată de faptul că OIDC este foarte flexibil în ceea ce privește autorizarea utilizatorului final. Astfel, a existat posibilitatea de a implementa suportul OIDC pe serverul nostru de autentificare actual.

OpenID Connect: autorizarea aplicațiilor interne de la soluții personalizate la standard

Calea noastră de implementare a propriului server OIDC

1) Am adus datele în forma necesară

Pentru integrarea OIDC, este necesar să aducem datele curente despre utilizatori într-o formă care să fie înțeleasă de standard. În OIDC, aceasta se numește Claims. Claims sunt, de fapt, câmpurile finale din baza de date a utilizatorilor (nume, email, telefon etc.). Există o listă standard de claims, iar tot ceea ce nu este inclus în această listă este considerat personalizat. Prin urmare, primul aspect la care trebuie să fiți atenți, dacă doriți să alegeți un furnizor OIDC existent, este capacitatea de personalizare convenabilă a noilor claims.

Grupul de claims este combinat într-un subansamblu numit – Scope. În timpul autorizării, se solicită accesul nu la claims specifice, ci anume la scopes, chiar dacă unele claims din scope nu sunt necesare.

2) Am implementat grant-urile necesare

Următoarea parte a integrării OIDC este alegerea și implementarea tipurilor de autorizare, numite granturi. Scenariul de interacțiune dintre aplicația aleasă și serverul de autorizare va depinde de grantul selectat. Schema aproximativă a alegerii grantului potrivit este prezentată în imaginea de mai jos.

OpenID Connect: autorizarea aplicațiilor interne de la soluții personalizate la standard

Pentru prima noastră aplicație, am folosit cel mai comun grant – Authorization Code. Diferența sa față de altele este că acesta este un proces în trei pași, adică trece printr-o verificare suplimentară. La început, utilizatorul trimite o solicitare de autorizare, primește un token – Authorization Code, apoi cu acest token, ca și cum ar fi un bilet de călătorie, solicită un token de acces. Toată interacțiunea principală a acestui scenariu de autorizare se bazează pe redirecționări între aplicație și serverul de autorizare. Puteți citi mai multe despre acest grant. aici.

OAuth susține conceptul că tokenurile de acces obținute după autorizare ar trebui să fie temporare și să fie schimbate, de preferat, în medie la fiecare 10 minute. Grantul Authorization Code este o verificare în trei pași prin redirecționări, iar a repeta acest pas la fiecare 10 minute nu este, să spunem, cea mai plăcută activitate. Pentru a rezolva această problemă, există un alt grant – Refresh Token, pe care l-am utilizat și noi. Aici este mai simplu. În timpul verificării cu un alt grant, pe lângă tokenul principal de acces, se emite și un alt token – Refresh Token, care poate fi folosit o singură dată și timpul său de viață este, de obicei, semnificativ mai lung. Cu acest Refresh Token, când TTL (Time to Live) al tokenului principal de acces expiră, cererea pentru un nou token de acces va veni pe endpoint-ul unui alt grant. Refresh Token-ul folosit este imediat invalidate. Această verificare este o procedură în două pași și poate fi realizată în background, fără a fi observată de utilizator.

3) Am configurat formatele de ieșire pentru datele utilizatorului

După ce granturile selectate sunt implementate și autorizarea funcționează, este important să menționăm obținerea datelor utilizatorului final. În OIDC există un endpoint separat pentru acest lucru, pe care, folosind tokenul de acces actual și dacă acesta este valid, pot fi solicitate datele utilizatorilor. Și dacă datele utilizatorului nu se schimbă atât de des, iar accesul la datele curente trebuie realizat frecvent, se poate ajunge la o soluție precum tokenurile JWT. Aceste tokenuri sunt, de asemenea, acceptate de standard. Un token JWT constă din trei părți: header (informații despre token), payload (orice date necesare) și signature (semnătura, tokenul fiind semnat de server, astfel încât sursa semnăturii să poată fi verificată ulterior).

În implementarea OIDC, tokenul JWT se numește id_token. Acesta poate fi solicitat împreună cu tokenul de acces obișnuit, iar tot ce rămâne este să se verifice semnătura. Serverul de autorizare are un endpoint separat pentru acest lucru, cu un grup de chei publice în format JWK. Și vorbind despre asta, merită menționat că există un alt endpoint, care se bazează pe standardul RFC5785 reflectă configurația curentă a serverului OIDC. Acesta conține toate adresele endpoint-urilor (inclusiv adresa grupului de chei publice utilizate pentru semnătură), declarațiile acceptate și scope-urile, algoritmii de criptare acceptați, granturile suportate etc.

De exemplu, în 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"
 ]
}

Astfel, prin intermediul id_token-ului se pot transmite toate revendicările necesare în payload-ul token-ului, fără a fi nevoie să se solicite date despre utilizator de la serverul de autorizare de fiecare dată. Dezavantajul acestei abordări este că modificările datelor utilizatorului din partea serverului nu ajung imediat, ci odată cu un nou token de acces.

Rezultatele implementării

Astfel, după implementarea propriului server OIDC și configurarea conexiunilor la acesta pe partea aplicațiilor, am rezolvat problema transmiterii informațiilor despre utilizatori.
Având în vedere că OIDC este un standard deschis, am avut posibilitatea de a alege un furnizor existent sau de a implementa un server. Am încercat Keycloak, care s-a dovedit foarte convenabil în configurare, iar după ajustarea și schimbarea configurațiilor de conectare pe partea aplicațiilor, este gata de utilizare. Pe partea aplicațiilor, rămâne doar să se schimbe configurațiile de conectare.

Vorbind despre soluțiile existente

În cadrul organizației noastre, ca primul server OIDC, am construit propriul nostru server, care a fost completat pe măsură ce a fost necesar. După o analiză detaliată a altor soluții disponibile, se poate spune că este un subiect discutabil. Avantajul implementării propriului server a fost temerile legate de lipsa funcționalităților necesare din partea furnizorilor, precum și existența unui sistem vechi care tenía diferite metode de autorizare personalizate pentru unele servicii și în care erau stocate deja multe date despre angajați. Totuși, soluțiile gata disponibile oferă conveniențe pentru integrare. De exemplu, în Keycloak există un sistem propriu de gestionare a utilizatorilor, iar datele sunt stocate direct în acesta, iar transferul utilizatorilor existenți acolo nu va fi o sarcină dificilă. Pentru aceasta, în Keycloak există un API, care va permite realizarea tuturor acțiunilor necesare pentru migrare.

Un alt exemplu interesant și certificat, în opinia mea, este Ory Hydra. Acesta este interesant prin faptul că este format din diferite componente. Pentru integrare, va trebui să legați serviciul dvs. de gestionare a utilizatorilor cu serviciul lor de autorizare și să extindeți pe măsură ce este necesar.

Keycloak și Ory Hydra nu sunt singurele soluții disponibile. Cel mai bine este să selectați o implementare certificată de OpenID Foundation. De obicei, astfel de soluții au un sigiliu de Certificare OpenID.

OpenID Connect: autorizarea aplicațiilor interne de la soluții personalizate la standard

De asemenea, nu uitați de furnizorii plătiți existenți, dacă nu doriți să vă gestionați propriul server OIDC. În prezent, există multe opțiuni bune.

Ce urmează

În viitorul apropiat, intenționăm să gestionăm traficul către serviciile interne într-un alt mod. Plănuim să ne migram actualul SSO pe un load balancer, folosind OpenResty ca proxy, bazat pe OAuth. Există deja multe soluții gata, de exemplu:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Materiale suplimentare

jwt.io – un serviciu bun pentru verificarea token-urilor JWT
openid.net/developers/certified — o listă cu implementările OIDC certificate

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster