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

Дадаць каментар