OpenID Connect: авторизація внутрішніх програм від самописних до стандарту

Кілька місяців тому я займався реалізацією сервера OpenID Connect для управління доступом сотень наших внутрішніх додатків. Від власних напрацювань, зручних на менших масштабах, ми перейшли до загальноприйнятого стандарту. Доступ через центральний сервіс значно полегшує монотонні операції, скорочує витрати на реалізацію авторизацій, дозволяє знаходити багато готових рішень і не ламати голову при розробці нових. У цій статті я розповім про цей перехід і про шишки, які ми встигли набити.

OpenID Connect: авторизація внутрішніх програм від самописних до стандарту

Давним-давно… З чого все починалося

Кілька років тому, коли внутрішніх програм стало занадто багато для ручного управління, ми написали програму для контролю доступів всередині компанії. Це була проста 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. Клейми по суті – це кінцеві поля у базі даних про користувачів (name, email, phone тощо). Існує стандартний список клеймів, А все, що не входить до цього списку, вважається кастомним. Тому перший момент, на який необхідно звернути увагу, якщо ви захочете вибрати існуючий провайдер 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) основного токена доступу, запит нового токена доступу прийде на endpoint вже іншого гранту. Використаний Refresh Token одразу обнулюється. Така перевірка є двокроковою і може бути виконана у фоні, непомітно для користувача.

3) Налаштували формати виведення даних користувача

Після того, як обрані гранти реалізовані, авторизація працює, варто згадати про отримання даних про кінцевого користувача. У OIDC є окремий endpoint для цього, на якому зі своїм поточним токеном доступу та за його актуальності можна запросити дані про користувачів. І якщо дані користувача не змінюються так часто, а ходити за поточними потрібно багато разів, можна прийти до такого рішення, як 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 Certification.

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

Додати коментар або відгук