Преди няколко месеца работих върху реализацията на OpenID Connect сървър за управление на достъпа до стотици от нашите вътрешни приложения. От собствените си разработки, удобни за по-малки мащаби, преминахме към общоприетия стандарт. Достъпът чрез централизирана услуга значително опростява монотонните операции, намалява разходите за реализиране на авторизации, позволява да се намерят много готови решения и не налага да се разчита на интуиция при разработването на нови. В тази статия ще разкажа за този преход и за трудностите, които срещнахме.

Отдавна… Как всичко започна
Преди няколко години, когато вътрешните приложения стана твърде много за ръчно управление, написахме приложение за контрол на достъпа в компанията. Това беше просто Rails приложение, което се свързваше с база данни с информация за служителите, където се настройваше достъпът до различни функционалности. Тогава също така стартирахме първото SSO, което се основаваше на проверка на токени от страна на клиента и сървъра за авторизация. Токенът се предаваше в криптиран вид с няколко параметъра и се сверяваше на сървъра за авторизация. Това не беше най-удобният вариант, тъй като за всяко вътрешно приложение трябваше да се опише значителен слой логика, а базите данни на служителите вовсе не се синхронизираха със сървъра за авторизация.
След известно време решихме да опростим задачата за централизирана авторизация. SSO беше преместен на балансер. С помощта на OpenResty на Lua добавихме шаблон, който проверяваше токените, знаеше в кое приложение отива заявката и можеше да провери дали достъпът е разрешен. Този подход значително опрости задачата за контрол на достъпа до вътрешните приложения – в кода на всяко приложение вече нямаше нужда да се описва допълнителна логика. В крайна сметка закрихме трафика навън, а самото приложение не знаеше нищо за авторизацията.
Но една от проблемите остава нерешена. Какво да правим с приложенията, които се нуждаят от информация за служителите? Можеше да се напише API за услугата за авторизация, но тогава щеше да се наложи да добавяме допълнителна логика за всяко такова приложение. Освен това, искахме да се освободим от зависимостта от едно от нашите саморъчни приложения, което по-късно щеше да се прехвърли в OpenSource, от нашия вътрешен сървър за авторизация. За него ще разкажем по-късно. Решението на двата проблема стана OAuth.
Към общоприетите стандарти
OAuth е ясен, общоприет стандарт за авторизация, но тъй като само неговата функционалност не е достатъчна, започнахме да разглеждаме и OpenID Connect (OIDC). OIDC сам по себе си е третата реализация на открития стандарт за аутентикация, който се е трансформирал в надстройка над протокола OAuth 2.0 (открит протокол за авторизация). Това решение решава проблема с липсата на данни за крайния потребител и също така дава възможност за смяна на доставчика на авторизация.
Но не избрахме конкретен доставчик и решихме да добавим интеграция с OIDC за нашия съществуващ сървър за авторизация. Предимство на това решение е, че OIDC е много гъвкав по отношение на авторизацията на крайния потребител. По този начин имаше възможност да се внедри поддръжка на OIDC на текущия ни сървър за авторизация.

Нашият път за реализация на собствен OIDC-сървър
1) Приведохме данните в необходимия вид
За интеграция с OIDC е необходимо да приведете текущите данни за потребителите в формат, разбираем за стандарта. В OIDC това се нарича Claims. Клеймите по същество са крайни полета в базата данни за потребителите (име, имейл, телефон и т.н.). Съществува , а всичко, което не попада в този списък, се счита за нестандартно. Затова първият момент, на който трябва да обърнете внимание, ако решите да изберете съществуващ OIDC доставчик, е възможността за удобна персонализация на новите клеймове.
Групата клеймове се обединява в следващото подмножество – Scope. При авторизацията се прави заявка за достъп не до конкретни клеймове, а именно до скоуповете, дори ако част от клеймовете в скоупа не са нужни.
2) Реализирахме нужните грантове
Следващата част от интеграцията на OIDC включва избора и реализация на типове авторизация, известни като грантове. От избрания грант зависи как ще протече взаимодействието между избраното приложение и сървъра за авторизация. Приблизителната схема за избор на необходимия грант е представена на изображението по-долу.

За нашето първо приложение използвахме най-разпространения грант - Authorization Code. Неговото отличие от другите е, че той е тристепенен, т.е. преминава допълнителна проверка. Първо потребителят прави заявка за разрешение за авторизация, получава токен - Authorization Code, и след това с този токен, като с билет за пътуване, иска токен за достъп. Всичкото основно взаимодействие в този сценарий на авторизация се основава на редиректи между приложението и сървъра за авторизация. Можете да прочетете повече за този грант. .
OAuth следва концепцията, че токените за достъп, получени след авторизация, трябва да са временни и е препоръчително да се сменят на всеки средно 10 минути. Грантът Authorization Code е тристепенна проверка чрез редиректи, като всяка 10 минути да се накланят на такъв етап е, честно казано, не много приятно за очите занятие. За решаване на този проблем съществува още един грант - Refresh Token, който също използвахме. Тук всичко е по-лесно. По време на проверката с друг грант, освен основния токен за достъп, се издава и втори - Refresh Token, който може да се използва само веднъж и с обикновено значително по-дълъг живот. С този Refresh Token, когато изтече TTL (Time to Live) на основния токен за достъп, заявката за нов токен за достъп ще бъде отправена към endpoint на друг грант. Използваният Refresh Token веднага се анулира. Тази проверка е двустепенна и може да бъде извършена на заден план, незабелязано за потребителя.
3) Конфигурирахме формати на изход на потребителски данни
След като избраните грантове са реализирани, удостоверяването работи, е важно да се спомене получаването на данни за крайния потребител. В OIDC има отделен endpoint за това, на който с текущия си токен за достъп и при неговата актуалност можете да поискате данни за потребителите. И ако данните на потребителя не се променят толкова често, а за текущите трябва да се отправят много заявки, може да се стигне до решение като JWT токените. Тези токени също се поддържат от стандарта. Самият JWT токен се състои от три части: header (информация за токена), payload (всякакви нужни данни) и signature (подпис, токенът се подписва от сървъра и по-късно може да се провери източникът на подписа му).
В имплементацията на OIDC JWT токенът се нарича id_token. Той може да бъде поискан заедно с обикновения токен за достъп и всичко, което остава – е да се провери подписът. За това съществува отделен endpoint на сървъра за удостоверяване с комплект публични ключове в формат . И казвайки това, е важно да споменем, че съществува още един endpoint, който на базата на стандарта отразява текущата конфигурация на OIDC сървъра. В него съдържа всички адреси на endpoint-овете (включително адреса на комплекта публични ключове, използвани за подписване), поддържаните клейми и области, използваните алгоритми за криптиране, поддържаните грантове и т.н.
Например в 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"
]
}По този начин с помощта на id_token можете да предавате всички нужни заявки в payload на токена и да не се налага всеки път да се обръщате към сървъра за удостоверяване, за да поискате информация за потребителя. Недостатък на този подход е, че промените в потребителските данни от сървъра не идват веднага, а с новия токен за достъп.
Резултати от реализацията
След като реализирахме собствения си OIDC сървър и настроихме свързването с него от страна на приложенията, решихме проблема с предаването на информация за потребителите.
Тъй като OIDC е открит стандарт, имаме възможност да изберем съществуващ доставчик или да реализираме собствен сървър. Опитахме Keycloak, който се оказа много удобен за конфигуриране; след настройка и промяна на конфигурациите за свързване от страна на приложенията, той е готов за работа. От страна на приложенията остава само да се променят конфигурациите за свързване.
Говорейки за съществуващите решения
В рамките на нашата организация като първи OIDC сървър създадохме собствена реализация, която се допълваше при необходимост. След подробно разглеждане на други готови решения, може да се каже, че е спорен момент. В полза на решението за реализация на собствен сървър стоят опасенията на доставчиците по отношение на отсъствието на необходимия функционал, а също и наличието на стара система, в която съществуваха различни персонализирани удостоверявания за някои услуги и вече бяха събрани доста данни за служителите. Въпреки това, готовите реализации предлагат удобства за интеграция. Например, в Keycloak има собствена система за управление на потребители и данните се съ хранят съвсем лесно в нея; прехвърлянето на вашите потребители там не е сложно. За това в Keycloak съществува API, което позволява да се направят всички необходими действия за прехвърляне.
Още един пример за сертифицирана, интересна, по мое мнение, реализация е Ory Hydra. Интересна е с това, че се състои от различни компоненти. За интеграция ще трябва да свържете своята услуга за управление на потребители с тяхната услуга за удостоверяване и да я разширявате при необходимост.
Keycloak и Ory Hydra не са единствените готови решения. Най-добре е да се избере сертифицирана реализация от OpenID Foundation. Обикновено тези решения имат значка за сертификация от OpenID.

Не забравяйте и за съществуващите платени доставчици, ако не искате да поддържате своя OIDC сервер. В днешно време има много добри опции.
Какво следва
В близко бъдеще планираме да ограничим трафика до вътрешните услуги по друг начин. Планираме да преместим текущото ни SSO на балансер с OpenResty в прокси, основан на OAuth. Има и много готови решения, например:
Допълнителни материали
– добър сервис за проверка на JWT токени
— списък на сертифицираните реализации на OIDC
Източник: habr.com
