Pár hónappal ezelőtt egy OpenID Connect szerver megvalósításával foglalkoztam, amely több száz belső alkalmazásunk hozzáférését kezeli. A kisebb méretekhez igazított saját megoldásainkról áttértünk az általánosan elfogadott szabványra. A központi szolgáltatáson keresztüli hozzáférés jelentősen leegyszerűsíti a monoton műveleteket, csökkenti a hitelesítések megvalósításának költségeit, lehetővé teszi sok kész megoldás felfedezését, és megkönnyíti az új megoldások fejlesztését. Ebben a cikkben mesélek erről az átalakulásról és azokról a nehézségekről, amelyeket tapasztaltunk.

Réges-régen… Hogyan kezdődött az egész
Néhány évvel ezelőtt, amikor a belső alkalmazások száma annyira megnövekedett, hogy már nem tudtuk kézzel kezelni, írtunk egy alkalmazást a vállalaton belüli hozzáférések ellenőrzésére. Ez egy egyszerű Rails-alkalmazás volt, amely kapcsolódott a munkavállalói adatokat tartalmazó adatbázishoz, ahol beállította a különböző funkciókhoz való hozzáférést. Akkor léptük meg az első SSO-t, amely a kliens és a hitelesítési szerver közötti tokenek ellenőrzésén alapult; a tokent titkosítva, több paraméterrel együtt adtuk át, és azt a hitelesítési szerveren ellenőriztük. Ez nem volt a legkényelmesebb megoldás, mivel minden belső alkalmazásnál jelentős logikai réteget kellett leírni, és a munkavállalói adatbázisok egyáltalán nem szinkronizálódtak a hitelesítési szerverrel.
Néhány idő elteltével úgy döntöttünk, hogy leegyszerűsítjük a centralizált hitelesítés feladatát. Az SSO-t áthelyeztük egy terheléselosztóra. Az OpenResty és a Lua segítségével egy sablont adtunk hozzá, amely ellenőrizte a tokeneket, tudta, hogy melyik alkalmazásra érkezik a kérés, és képes volt ellenőrizni a hozzáférést. Ez a megközelítés jelentősen leegyszerűsítette a belső alkalmazások hozzáférés-ellenőrzésének feladatát — az egyes alkalmazások kódjában már nem kellett az extra logikát leírni. Végül a forgalmat külsőleg lezártuk, és az alkalmazás önmagáról semmit nem tudott a hitelesítésről.
Azonban egy probléma megoldatlan maradt. Mi legyen a munkavállalókkal kapcsolatos információkra szükséges alkalmazásokkal? Írhattunk volna egy API-t az azonosítási szolgáltatáshoz, de akkor minden egyes ilyen alkalmazásnál extra logikát kellett volna hozzáadnunk. Ráadásul szeretnénk megszabadulni a saját fejlesztésű alkalmazásunktól, amely a jövőben az OpenSource-ra helyeződne át, a belső azonosító szerverünktől. Erről majd később mesélünk. A megoldás mindkét problémára az OAuth lett.
Elterjedt szabványok
Az OAuth egy érthető, elterjedt azonosítási szabvány, de mivel a funkcionalitása önmagában nem elegendő, az OpenID Connect (OIDC) is a látókörbe került. Az OIDC alapvetően egy nyílt hitelesítési szabvány harmadik verziója, amely az OAuth 2.0 (nyílt azonosítási protokoll) protokollra épül. Ez a megoldás megoldja a végfelhasználó adataival kapcsolatos hiányosságokat, és lehetővé teszi az azonosító szolgáltató cseréjét.
Azonban nem választottunk ki konkrét szolgáltatót, és úgy döntöttünk, hogy a meglévő azonosító szerverünk integrálását OIDC-vel bővítjük. Ennek a megoldásnak az előnye a végfelhasználók azonosításának rugalmassága az OIDC-ben. Így lehetőség nyílt az OIDC támogatásának bevezetésére a jelenlegi azonosító szerverünkön.

A saját OIDC-szerverünk megvalósításának útja
1) Az adatokat a kívánt formára hoztuk
Az OIDC integrációhoz a meglévő felhasználói adatokat a szabvány által érthető formára kell hozni. Az OIDC-ben ezt Claims-nak hívják. A kérések lényegében a felhasználói adatbázis végső mezői (név, e-mail, telefon stb.). Létezik , és ami nem szerepel ebben a listában, az egyedi. Ezért az első dolog, amire figyelni kell, ha egy meglévő OIDC-szolgáltatót kíván választani – az új kérelmek kényelmes testreszabhatósága.
A kérelmek csoportokba szerveződnek – Scope. Az azonosítás során nem konkrét kérelmekhez kérnek hozzáférést, hanem éppen a scope-hoz, még ha a scope-ból bizonyos kérelmekre nincs is szükség.
2) A szükséges jogosultságokat megvalósítottuk
A következő lépése az OIDC integrációnak az engedélyezési típusok, az úgynevezett grantok kiválasztása és megvalósítása. A kiválasztott grant határozza meg a kiválasztott alkalmazás és az engedélyezési szerver közötti kölcsönhatás további forgatókönyvét. A szükséges grant kiválasztásának példás ábrája az alábbiakban látható.

Az első alkalmazásunkhoz a legelterjedtebb grantot használtuk – az Authorization Code-ot. Ennek a többitől való eltérése, hogy háromlépcsős, azaz további ellenőrzést igényel. Először a felhasználó kérvényt intéz az engedélyezési jogosultságra, megkapja a tokent – Authorization Code-ot, majd ezzel a tokent használva, mint egy jegyet a belépéshez, kér egy hozzáférési tokent. Az engedélyezési forgatókönyv minden fő kölcsönhatása átirányításokra épül az alkalmazás és az engedélyezési szerver között. Részletesebben erről a grantról itt lehet olvasni. .
Az OAuth ragaszkodik ahhoz az elképzeléshez, hogy az engedélyezés után megszerzett hozzáférési tokeneknek ideigleneseknek kell lenniük, és ideális esetben 10 percenként cserélni kell őket. Az Authorization Code grant háromlépcsős ellenőrzést valósít meg átirányításokon keresztül, ezt 10 percenként megcsinálni, hogy őszinte legyek, nem a legszebb dolog a szemnek. Ennek a problémának a megoldására létezik egy másik grant – a Refresh Token, amelyet nálunk is alkalmaztunk. Itt minden egyszerűbb. A másik grant ellenőrzése során az alapvető hozzáférési token mellett egy másik – Refresh Token is kiadásra kerül, amelyet csak egyszer lehet felhasználni, és az élettartama általában lényegesen hosszabb. Ezzel a Refresh Token-nel, amikor a fő hozzáférési token TTL-je (Time to Live) lejár, az új hozzáférési token kérés már egy másik grant végpontjára érkezik. A felhasznált Refresh Token azonnal érvénytelenítésre kerül. Ez a vértezés kétlépcsős és a háttérben, a felhasználó számára észrevétlenül valósulhat meg.
3) Beállítottuk a felhasználói adatok kimeneti formátumait
Miután a kiválasztott grantok megvalósultak és az engedélyezés működik, érdemes megemlíteni a végfelhasználói adatok megszerzését. Az OIDC-nek van egy külön endpoint-ja erre, amelynél a jelenlegi hozzáférési token használatával, amennyiben az érvényes, kérhetők a felhasználói adatok. Ha a felhasználói adatok nem változnak túl gyakran, de sokszor szükség van a friss adatokra, akkor egy JWT-token használata megfelelő megoldás lehet. Ezek a tokenek a szabvány által is támogatottak. Maga a JWT-token három részből áll: header (token információ), payload (bármilyen szükséges adat) és signature (aláírás, a token a szerver által van aláírva, és később ellenőrizhető a forrása).
Az OIDC implementációban a JWT-tokent id_token-nak hívják. Ezt kérhetjük a szokásos hozzáférési tokennel együtt, és az egyetlen teendő, hogy ellenőrizzük az aláírást. Az engedélyezési szerverhez egy külön endpoint kapcsolódik, amely a nyilvános kulcsok párost használja . Erről beszélve érdemes megemlíteni, hogy létezik egy másik endpoint, amely a szabvány alapján tükrözi az OIDC-szerver aktuális konfigurációját. Ez tartalmazza az összes endpoint címét (beleértve a nyilvános kulcsok párosának címét, amelyeket az aláíráshoz használnak), a támogatott claim-eket és scope-okat, a használt titkosítási algoritmusokat, a támogatott grantokat stb.
Például a Google esetében:
{
"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"
]
}Így az id_token segítségével át lehet adni az összes szükséges claimt a token payload-jában, anélkül, hogy minden alkalommal a hitelesítési szerverhez kellene fordulni a felhasználói adatok megkéréséhez. Ennek a megközelítésnek a hátránya, hogy a felhasználói adatok szerver általi módosítása nem azonnal érkezik, hanem az új hozzáférési tokennel együtt.
A megvalósítás összegzése
Miután megvalósítottuk a saját OIDC szerverünket, és beállítottuk a hozzá csatlakozó alkalmazásokat, megoldottuk a felhasználói információk továbbításának problémáját.
Mivel az OIDC nyílt szabvány, lehetőségünk nyílt meglévő szolgáltató kiválasztására vagy saját szerver megvalósítására. Kipróbáltuk a Keycloakot, amely nagyon kényelmes volt a konfigurálás szempontjából; a beállítások és az alkalmazások kapcsolódási konfigurációinak módosítása után készen állt a használatra. Az alkalmazások oldalán csak a kapcsolódási konfigurációt kell megváltoztatni.
A meglévő megoldásokról beszélve
Szervezetünk keretein belül első OIDC szerverként a saját megvalósításunkat hoztuk létre, amelyet szükség szerint bővítettünk. Más kész megoldások alapos áttekintése után elmondható, hogy ez vitatott kérdés. A saját szerver megvalósítása mellett a szolgáltatók aggodalmai is döntő szerepet játszottak, mivel hiányzott a szükséges funkcionalitás, és már egy régi rendszer is létezett, amely különböző egyedi hitelesítéseket tartalmazott bizonyos szolgáltatásokhoz, valamint már sok adat állt rendelkezésre a munkatársakról. Azonban a kész megoldások kényelmet nyújtanak az integrációhoz. Például a Keycloak saját felhasználókezelő rendszerével rendelkezik, és az adatok közvetlenül ott tárolódnak, a meglévő felhasználókat pedig nem nehéz oda átkonvertálni. Ehhez a Keycloak API-t kínál, amely lehetővé teszi a szükséges lépések teljes körű végrehajtását az átvitelhez.
Egy másik példája a tanúsított, szerintem érdekes megvalósításnak az Ory Hydra. Azért érdekes, mert különböző komponensekből áll. Az integrációhoz össze kell kötni a saját felhasználókezelő szolgáltatásodat az ő hitelesítési szolgáltatásukkal, és igény szerint kell bővíteni.
A Keycloak és az Ory Hydra nem az egyetlen kész megoldás. A legjobb egy tanúsított OpenID Foundation megvalósítást választani. Általában az ilyen megoldások rendelkeznek OpenID tanúsítvány jelvényt.

Ne feledje el a létező fizetős szolgáltatókat, ha nem szeretné saját OIDC szervert üzemeltetni. Jelenleg sok jó lehetőség áll rendelkezésre.
Mi következik
A közeljövőben más módon fogjuk szabályozni a forgalmat a belső szolgáltatásokhoz. Az SSO-t, amelyet jelenleg használunk, nyitott proxy-balanszeren keresztül OpenResty-vel fogjuk átállítani, amelynek alapja az OAuth. Itt szintén sok kész megoldás létezik, például:
További anyagok
– jó szolgáltatás a JWT-tokenek ellenőrzésére
— a certifikált OIDC megoldások listája
Forrás: habr.com
