OpenID Connect: autoryzacja aplikacji wewnętrznych od niestandardowych do standardowych

Kilka miesięcy temu wdrażałem serwer OpenID Connect do zarządzania dostępem dla setek naszych wewnętrznych aplikacji. Z własnych opracowań, wygodnych na mniejszą skalę, przeszliśmy do ogólnie przyjętego standardu. Dostęp przez serwis centralny znacznie upraszcza monotonne operacje, zmniejsza koszty wdrażania uprawnień, pozwala znaleźć wiele gotowych rozwiązań i nie męczyć się przy opracowywaniu nowych. W tym artykule opowiem o tym przejściu i nierównościach, które udało nam się wypełnić.

OpenID Connect: autoryzacja aplikacji wewnętrznych od niestandardowych do standardowych

Dawno temu... Jak to się wszystko zaczęło

Kilka lat temu, gdy wewnętrznych aplikacji do ręcznej kontroli było zbyt dużo, napisaliśmy aplikację do kontroli dostępu w firmie. Była to prosta aplikacja Railsowa, która łączyła się z bazą danych z informacjami o pracownikach, gdzie konfigurowano dostęp do różnych funkcjonalności. Jednocześnie podnieśliśmy pierwsze SSO, które polegało na weryfikacji tokenów od strony klienta i serwera autoryzacyjnego, token był przesyłany w postaci zaszyfrowanej z kilkoma parametrami i weryfikowany na serwerze autoryzacyjnym. Nie była to najwygodniejsza opcja, ponieważ każda aplikacja wewnętrzna musiała opisywać pokaźną warstwę logiki, a bazy pracowników były w pełni zsynchronizowane z serwerem autoryzacyjnym.

Po pewnym czasie postanowiliśmy uprościć zadanie scentralizowanej autoryzacji. SSO zostało przeniesione do balancera. Z pomocą OpenResty do Lua dodano szablon, który sprawdzał tokeny, wiedział, do której aplikacji kierowane jest żądanie i mógł sprawdzić, czy jest tam dostęp. Takie podejście znacznie uprościło zadanie kontrolowania dostępu do wewnętrznych aplikacji - w kodzie każdej aplikacji nie było już potrzeby opisywania dodatkowej logiki. W rezultacie zamknęliśmy ruch z zewnątrz, a sama aplikacja nie wiedziała nic o autoryzacji.

Jednak jeden problem pozostał nierozwiązany. Co z aplikacjami, które potrzebują informacji o pracownikach? Można było napisać API dla usługi autoryzacyjnej, ale wtedy trzeba by było dodać dodatkową logikę dla każdej takiej aplikacji. Ponadto chcieliśmy pozbyć się uzależnienia od jednej z naszych samodzielnie napisanych aplikacji, zorientowanej w przyszłości do tłumaczenia na OpenSource, na naszym wewnętrznym serwerze autoryzacyjnym. Porozmawiamy o tym innym razem. Rozwiązaniem obu problemów była OAuth.

do wspólnych standardów

OAuth to zrozumiały, ogólnie akceptowany standard autoryzacji, ale ponieważ sama jego funkcjonalność to za mało, od razu zaczęto rozważać OpenID Connect Connect (OIDC). Sam OIDC jest trzecią implementacją otwartego standardu uwierzytelniania, który wpłynął do dodatku w protokole OAuth 2.0 (otwarty protokół autoryzacji). Rozwiązanie to zamyka problem braku danych o użytkowniku końcowym, a także umożliwia zmianę dostawcy autoryzacji.

Nie wybraliśmy jednak konkretnego dostawcy i postanowiliśmy dodać integrację z OIDC dla naszego istniejącego serwera autoryzacyjnego. Na korzyść tej decyzji przemawiał fakt, że OIDC jest bardzo elastyczne w zakresie autoryzacji użytkowników końcowych. Tym samym możliwe było zaimplementowanie obsługi OIDC na Państwa obecnym serwerze autoryzacyjnym.

OpenID Connect: autoryzacja aplikacji wewnętrznych od niestandardowych do standardowych

Nasz sposób na wdrożenie własnego serwera OIDC

1) Doprowadził dane do żądanego formularza

Aby zintegrować OIDC, konieczne jest sprowadzenie aktualnych danych użytkownika do postaci zrozumiałej dla standardu. W OIDC nazywa się to roszczeniami. Roszczenia to zasadniczo końcowe pola w bazie danych użytkowników (imię i nazwisko, adres e-mail, telefon itp.). istnieje standardowa lista znaczków, a wszystko, czego nie ma na tej liście, jest uważane za niestandardowe. Dlatego pierwszym punktem, na który należy zwrócić uwagę, jeśli chce się wybrać istniejącego dostawcę OIDC, jest możliwość wygodnej personalizacji nowych marek.

Grupa cech łączy się w następujący podzbiór - Zakres. Podczas autoryzacji żądany jest dostęp nie do konkretnych marek, ale do zakresów, nawet jeśli niektóre marki z zakresu nie są potrzebne.

2) Wdrożył niezbędne dotacje

Kolejnym etapem integracji OIDC jest wybór i implementacja typów uprawnień, tzw. grantów. Dalszy scenariusz interakcji między wybraną aplikacją a serwerem autoryzacyjnym będzie uzależniony od wybranego grantu. Przykładowy schemat wyboru odpowiedniego grantu przedstawia poniższy rysunek.

OpenID Connect: autoryzacja aplikacji wewnętrznych od niestandardowych do standardowych

W przypadku naszej pierwszej aplikacji wykorzystaliśmy najpopularniejszy grant, kod autoryzacyjny. Różni się od innych tym, że jest trzyetapowy, tj. przechodzi dodatkowe testy. Najpierw użytkownik występuje z prośbą o zezwolenie na autoryzację, otrzymuje token - Kod Autoryzacyjny, następnie tym tokenem, jak z biletem na podróż, żąda tokena dostępu. Cała główna interakcja tego skryptu autoryzacyjnego opiera się na przekierowaniach między aplikacją a serwerem autoryzacyjnym. Możesz przeczytać więcej o tej dotacji tutaj.

OAuth wyznaje koncepcję, że tokeny dostępu uzyskane po autoryzacji powinny być tymczasowe i zmieniać się najlepiej średnio co 10 minut. Przyznanie kodu autoryzacyjnego to trzyetapowa weryfikacja poprzez przekierowania, co 10 minut obrócenie takiego kroku, szczerze mówiąc, nie należy do najprzyjemniejszych zadań dla oczu. Aby rozwiązać ten problem, istnieje inny grant - Refresh Token, z którego skorzystaliśmy również w naszym kraju. Tutaj wszystko jest prostsze. Podczas weryfikacji z kolejnego grantu, oprócz głównego access tokena, wydawany jest jeszcze jeden – Refresh Token, którego można użyć tylko raz, a jego żywotność jest zwykle znacznie dłuższa. Dzięki temu tokenowi odświeżania, gdy skończy się TTL (czas życia) głównego tokena dostępu, prośba o nowy token dostępu dotrze do punktu końcowego innego grantu. Wykorzystany token odświeżania jest natychmiast resetowany do zera. Ta kontrola jest dwuetapowa i może być przeprowadzona w tle, niezauważalnie dla użytkownika.

3) Skonfiguruj niestandardowe formaty danych wyjściowych

Po wdrożeniu wybranych grantów autoryzacja działa, warto wspomnieć o pozyskiwaniu danych o użytkowniku końcowym. OIDC ma do tego oddzielny punkt końcowy, w którym możesz zażądać danych użytkownika za pomocą bieżącego tokena dostępu i jeśli jest on aktualny. A jeśli dane użytkownika nie zmieniają się tak często, a trzeba wielokrotnie śledzić aktualne, można przyjść na takie rozwiązanie jak tokeny JWT. Tokeny te są również obsługiwane przez standard. Sam token JWT składa się z trzech części: nagłówka (informacja o tokenie), payloadu (wszelkie niezbędne dane) oraz podpisu (sygnatura, token jest podpisany przez serwer i można później sprawdzić źródło jego podpisu).

W implementacji OIDC token JWT nosi nazwę id_token. Można go zażądać wraz z normalnym tokenem dostępu i pozostaje tylko zweryfikować podpis. Serwer autoryzacji ma do tego osobny punkt końcowy z wiązką kluczy publicznych w formacie J.W.K.. A mówiąc o tym, warto wspomnieć, że jest jeszcze jeden punkt końcowy, który bazuje na normie RFC5785 odzwierciedla bieżącą konfigurację serwera OIDC. Zawiera wszystkie adresy punktów końcowych (w tym adres pęku kluczy publicznych używanego do podpisywania), obsługiwane marki i zakresy, zastosowane algorytmy szyfrowania, obsługiwane granty itp.

Na przykład w 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"
 ]
}

W ten sposób za pomocą id_token możesz przenieść wszystkie niezbędne cechy charakterystyczne do ładunku tokena i nie kontaktować się za każdym razem z serwerem autoryzacyjnym w celu żądania danych użytkownika. Wadą tego podejścia jest to, że zmiana danych użytkownika z serwera nie następuje od razu, ale wraz z nowym tokenem dostępu.

Wyniki wdrożenia

Tak więc po wdrożeniu własnego serwera OIDC i skonfigurowaniu połączeń z nim po stronie aplikacji rozwiązaliśmy problem przesyłania informacji o użytkownikach.
Ponieważ OIDC jest standardem otwartym, mamy możliwość wyboru istniejącego dostawcy lub implementacji serwera. Wypróbowaliśmy Keycloak, który okazał się bardzo wygodny w konfiguracji, po skonfigurowaniu i zmianie konfiguracji połączeń po stronie aplikacji jest gotowy do działania. Od strony aplikacji pozostaje tylko zmiana konfiguracji połączeń.

Rozmowa o istniejących rozwiązaniach

W ramach naszej organizacji, jako pierwszy serwer OIDC, zmontowaliśmy własne wdrożenie, które w razie potrzeby uzupełnialiśmy. Po szczegółowym przeglądzie innych gotowych rozwiązań możemy stwierdzić, że jest to kwestia sporna. Na korzyść decyzji o wdrożeniu własnego serwera przemawiały obawy dostawców dotyczące braku niezbędnej funkcjonalności, a także obecności starego systemu, w którym były różne niestandardowe uprawnienia do niektórych usług i całkiem sporo danych o pracownikach było już przechowywanych. Jednak w gotowych implementacjach są udogodnienia dla integracji. Na przykład Keycloak ma własny system zarządzania użytkownikami, a dane są w nim przechowywane bezpośrednio i nie będzie trudno wyprzedzić tam użytkowników. Aby to zrobić, Keycloak posiada API, które pozwoli Ci w pełni przeprowadzić wszystkie niezbędne czynności związane z transferem.

Kolejnym przykładem certyfikowanej, ciekawej moim zdaniem realizacji jest Ory Hydra. Jest to interesujące, ponieważ składa się z różnych elementów. Aby zintegrować, musisz połączyć swoją usługę zarządzania użytkownikami z ich usługą autoryzacji i rozszerzyć ją w razie potrzeby.

Keycloak i Ory Hydra to nie jedyne gotowe rozwiązania. Najlepiej wybrać wdrożenie certyfikowane przez OpenID Foundation. Rozwiązania te zwykle mają plakietkę certyfikacyjną OpenID.

OpenID Connect: autoryzacja aplikacji wewnętrznych od niestandardowych do standardowych

Nie zapomnij również o istniejących płatnych dostawcach, jeśli nie chcesz zachować swojego serwera OIDC. Dzisiaj istnieje wiele dobrych opcji.

Co dalej

W najbliższym czasie w inny sposób zamkniemy ruch do usług wewnętrznych. Planujemy przenieść nasze obecne SSO na balancer za pomocą OpenResty do proxy opartego na OAuth. Jest tu już wiele gotowych rozwiązań, na przykład:
https://github.com/bitly/oauth2_proxy
https://github.com/ory/oathkeeper
https://github.com/keycloak/keycloak-gatekeeper

Dodatkowe materiały

jwt.io – dobry serwis do walidacji tokenów JWT
openid.net/developers/certified - lista certyfikowanych wdrożeń OIDC

Źródło: www.habr.com

Dodaj komentarz