OpenID Connect: авторизация на вътрешни приложения от саморъчно разработени до стандартни

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

OpenID Connect: авторизация на вътрешни приложения от саморъчно разработени до стандартни

Отдавна… Как започна всичко

Преди няколко години, когато броят на вътрешните приложения стана твърде голям за ръчно управление, написахме приложение за контрол на достъпа в компанията. Това беше простo Rails-приложение, което се свързваше с база данни с информация за служителите, където се настройваше достъп до различен функционал. Тогава също така инсталирахме първото SSO, което се базираше на проверка на токени между клиента и сървъра за авторизация; токенът се предаваше в криптиран вид с няколко параметъра и се проверяваше на сървъра за авторизация. Това не беше най-удобният вариант, тъй като за всяко вътрешно приложение трябваше да се опише значителен слой логика, а базите с информация за служителите не синхронизираха с сървъра за авторизация.

След известно време решихме да опростим задачата за централизирана авторизация. Прехвърлихме SSO на балансер. С помощта на OpenResty на Lua добавихме шаблон, който проверяваше токените, знаеше в кое приложение отива заявката и можеше да проверява дали има достъп до него. Такъв подход значително опрости задачата за контрол на достъпа до вътрешните приложения — в кода на всяко приложение вече не беше нужно да се описва допълнителна логика. В крайна сметка затворихме трафика навън, а самото приложение не знаеше нищо за авторизацията.

Въпреки това, една от проблемите остана нерешена. Какво да правим с приложенията, които се нуждаят от информация за служителите? Можехме да напишем API за услугата за удостоверяване, но тогава щеше да се наложи да добавим допълнителна логика за всяко такова приложение. Освен това искахме да се избавим от зависимостта от едно наше собствено приложение, което в бъдеще ще бъде ориентирано към превод в OpenSource, от нашия вътрешен сървър за удостоверяване. За него ще разкажем някой друг път. Решението на двата проблема стана OAuth.

К общоприетите стандарти

OAuth е ясен, общоприет стандарт за удостоверяване, но тъй като само неговата функционалност не е достатъчна, започнаха да разглеждат веднага и OpenID Connect (OIDC). Самият OIDC е третата реализация на открития аутентификационен стандарт, който е надстройка над протокола OAuth 2.0 (открит протокол за удостоверяване). Такова решение решава проблема с липсата на данни за крайния потребител, както и предоставя възможност за смяна на доставчика на удостоверяване.

Въпреки това, не избрахме конкретен доставчик и решихме да добавим интеграция с OIDC за нашия съществуващ сървър за удостоверяване. В полза на такова решение говори факта, че OIDC е много гъвкав в контекста на удостоверяване на крайния потребител. По този начин се създаде възможност да се внедри поддръжка на OIDC на текущия ни сървър за удостоверяване.

OpenID Connect: авторизация на вътрешни приложения от саморъчно разработени до стандартни

Нашият път за реализиране на собствен OIDC-сървър

1) Приведохме данните в необходимия вид

За интеграция на OIDC е необходимо текущите данни за потребителите да бъдат приведени в вид, разбираем за стандарта. В OIDC това се нарича Claims. Клеймовете по същество са крайни полета в базата данни за потребителите (име, имейл, телефон и т.н.). Съществува стандартен списък с клеймове, а всичко, което не попада в този списък, се счита за персонализирано. Ето защо първият момент, на който трябва да обърнете внимание, ако решите да изберете съществуващ OIDC-доставчик, е възможността за удобна персонализация на новите клеймове.

Групата клеймове се обединява в следното подмножество – Scope. При удостоверяване се изпраща заявка за достъп не към конкретни клеймове, а именно към скоуповете, дори ако част от клеймовете в скоупа не са нужни.

2) Реализирахме нужните грантове

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

OpenID Connect: авторизация на вътрешни приложения от саморъчно разработени до стандартни

За нашето първо приложение използвахме най-разпространения грант – Authorization Code. Неговото отличие от другите е, че той е тристепенен, т.е. преминава допълнителна проверка. Първо потребителят изпраща заявка за разрешение за авторизация, получава токен – Authorization Code, след което с този токен, както с билет за пътуване, иска токен за достъп. Всичкото основно взаимодействие в този сценарий на авторизация е основано на редиректи между приложението и авторизационния сървър. Повече информация за този грант може да се намери тук..

OAuth се придържа към концепцията, че токените за достъп, получени след авторизация, трябва да бъдат временни и да се променят, идеално, на всеки 10 минути. Грантът Authorization Code представлява тристепенна проверка чрез редиректи, като всяка 10 минути такъв етап да се изпълнява честно казано, не е най-приятното за очите занимание. За решаване на този проблем съществува още един грант – Refresh Token, който също така използвахме. Тук всичко е по-просто. По време на проверката с друг грант, освен основния токен за достъп, се издава още един – Refresh Token, който може да се използва само веднъж и животът му, обикновено, е значително по-дълъг. С този Refresh Token, когато изтече TTL (Time to Live) на основния токен за достъп, заявката за нов токен за достъп ще дойде на крайната точка на другия грант. Използваният Refresh Token веднага се анулира. Тази проверка е двустепенна и може да се извърши на заден план, незабелязано за потребителя.

3) Настроихме формати за извеждане на потребителски данни

След като избраните грантове са реализирани и авторизацията работи, е важно да се спомене получаването на данни за крайния потребител. В OIDC има отделен endpoint за това, на който с текущия си access токен и при валидност можете да поискате данни за потребителите. И ако данните на потребителя не се променят толкова често, а е необходимо да се получават често, може да се достигне до такова решение, като JWT токени. Тези токени също се поддържат от стандарта. Самият JWT токен се състои от три части: header (информация за токена), payload (необходими данни) и signature (подпис, токенът се подписва от сървъра и впоследствие може да се провери източникът на този подпис).

В имплементацията на OIDC JWT токенът се нарича id_token. Той може да бъде поискан заедно с обикновения токен за достъп и всичко, което остава - е да се провери подписът. За тази цел сървърът за авторизация има отделен endpoint с държава на публичните ключове в формат JWK. И говорейки за това, е полезно да се спомене, че съществува още един endpoint, който на базата на стандарта RFC5785 отразява текущата конфигурация на 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.

OpenID Connect: авторизация на вътрешни приложения от саморъчно разработени до стандартни

Съществуват много добри платени доставчици, ако не искате да управлявате своя OIDC сървър. В днешно време има много добри опции.

Какво следва

В близко бъдеще планираме да затворим трафика до вътрешни услуги по друг начин. Планираме да преместим текущото си SSO на балансера с помощта на OpenResty на прокси, основан на OAuth. И тук също вече съществуват много готови решения, например:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Допълнителни материали

jwt.io – чудесна услуга за проверка на JWT токени
openid.net/developers/certified — списък на сертифицираните реализации на OIDC

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster