Jak dobrać bibliotekę do uwierzytelniania: Auth.js, Keycloak, OAuth2 i typowe pułapki

0
45
Rate this post

Chcesz świadomie dobrać bibliotekę lub serwer do uwierzytelniania (Auth.js, Keycloak, własny OAuth2) i uniknąć typowych błędów architektonicznych oraz bezpieczeństwa. Szukasz kryteriów jakości, punktów kontrolnych i praktycznych wskazówek, które ułatwią wybór i wdrożenie.

porównanie Auth.js i Keycloak, implementacja OAuth2 w praktyce, wybór biblioteki do logowania, typowe błędy we wdrażaniu SSO, best practices zarządzania sesją, integracja z identity providerem, bezpieczeństwo tokenów JWT, audyt konfiguracji uwierzytelniania

Jak doprecyzować wymagania zanim wybierzesz bibliotekę do uwierzytelniania

Kontekst biznesowy i regulacyjny

Pierwszy punkt kontrolny to odpowiedź na pytanie: co właściwie chronisz i w jakim otoczeniu prawnym działasz. Wybór pomiędzy Auth.js, Keycloak czy własnym serwerem OAuth2 jest wtórny wobec tego, jakie dane przetwarza aplikacja i jakie ryzyka są akceptowalne biznesowo. Inaczej projektuje się logowanie do prostego panelu raportowego dla kilku użytkowników wewnętrznych, a inaczej do aplikacji medycznej dostępnej globalnie.

Minimum to policzenie i opisanie kilku parametrów:

  • Skala użytkowników – kilkunastu adminów, setki pracowników, czy dziesiątki tysięcy klientów?
  • Wrażliwość danych – dane osobowe zwykłe, dane finansowe, dane medyczne, tajemnice przedsiębiorstwa?
  • Regulacje – RODO, PSD2, HIPAA, wewnętrzne normy bezpieczeństwa korporacji?
  • Geografia – użytkownicy z UE, USA, innych regionów z własnymi wymogami prawnymi?
  • Audytowalność – czy logowanie i autoryzacja muszą być raportowane do audytów zewnętrznych?

Jeśli operujesz w mocno regulowanym środowisku (bankowość, medycyna, sektor publiczny), punkt kontrolny jest prosty: prosta biblioteka „in-app” zwykle nie wystarczy. Organizacje tego typu często wymagają centralnego IdP, szczegółowych logów, zaawansowanych polityk hasłowych i MFA – to obszar bardziej dla Keycloak lub IdP klasy enterprise niż dla samego Auth.js.

Jeżeli natomiast budujesz produkt SaaS dla MŚP, bez specyficznych regulacji i z umiarkowaną wrażliwością danych, sensowne staje się szukanie rozwiązania możliwie prostego, dobrze wkomponowanego w wybrany framework – tu Auth.js może być „minimum rozsądku”, o ile inne kryteria nie wymuszą cięższej artylerii.

Profil aplikacji (SPA, SSR, mobilna, mikroserwisy)

Drugi krytyczny wymiar to architektura aplikacji. Ten sam mechanizm logowania będzie działał inaczej w monolicie renderującym HTML na serwerze, inaczej w SPA, a jeszcze inaczej w środowisku mikroserwisów czy aplikacji mobilnych. Błędny dobór podejścia na tym etapie prowadzi potem do prowizorek: kopiowania tokenów w dziwnych miejscach, API bez spójnych zasad, czy mieszania sesji i JWT bez zrozumienia.

Dla typowych scenariuszy można wyróżnić kilka profili:

  • SSR / full-stack framework (np. Next.js) – logika na serwerze, możliwość bezpiecznego użycia ciasteczek HTTP-only, łatwiejsze zarządzanie sesją po stronie backendu. Auth.js jest tu naturalnym kandydatem, jeśli nie potrzebujesz centralnego IdP.
  • SPA + API – frontend (React, Vue, Angular) jako osobny klient do API. Często bazuje na OAuth2/OIDC z tokenami dostępu, co naturalnie prowadzi do potrzeby serwera autoryzacji (Keycloak lub zewnętrzny IdP). Auth.js może nadal pomóc, ale nie rozwiąże problemu architektury całego ekosystemu.
  • Aplikacje mobilne – zwykle lepiej integrują się z zewnętrznym IdP po OAuth2/OIDC (PKCE), używając natywnego przeglądarkowego flow. Kluczowe jest wtedy posiadanie solidnego serwera autoryzacji, nie tylko biblioteki w aplikacji webowej.
  • Mikroserwisy – wymagają centralnego źródła prawdy o tożsamości. Tu Keycloak lub dedykowany Authorization Server z OAuth2/OIDC to niemal standard, a „biblioteka do logowania” w pojedynczym serwisie przestaje wystarczać.

Jeśli projekt przewiduje jeden serwer i jedną aplikację webową, prostsze rozwiązania mają duże szanse być wystarczające. Jeśli jednak na roadmapie są kolejne usługi, mikroserwisy, aplikacje mobilne – sygnał ostrzegawczy: wybór „na skróty” w postaci lokalnej implementacji logowania szybko stanie się blokadą.

Wymagania dotyczące użytkowników i ról

Trzeci wymiar decyzji to model użytkownika, ról i uprawnień. Mechanizm „login + hasło” to tylko wierzchołek góry lodowej. Istotne jest, kto może się rejestrować, jak przydzielane są role, czy występują organizacje/tenanty, czy potrzebna jest delegacja uprawnień między kontami, a także czy dane użytkowników mają być współdzielone między systemami.

Kilka pytań kontrolnych, które warto zadać przed wyborem technologii:

  • Czy konto użytkownika jest współdzielone między kilkoma aplikacjami (np. panel klienta, panel partnera, aplikacja mobilna)?
  • Czy istnieje potrzeba wielopoziomowych ról (rola globalna, rola w ramach organizacji, rola w ramach projektu)?
  • Czy przewidujesz logowanie przez zewnętrznych providerów (Google, Microsoft, firmowy Azure AD, SAML/ADFS)?
  • Czy uprawnienia mają być konfigurowalne przez administratorów biznesowych, czy twardo zakodowane w aplikacji?
  • Czy konieczny jest audyt operacji (kto komu zmienił rolę, kiedy zabrano dostęp, na jakiej podstawie)?

Prosty panel B2B dla kilkunastu osób często zadowoli się kilkoma rolami typu „admin”, „user”, trzymanymi bezpośrednio w bazie aplikacji. Do tego wystarczy Auth.js lub nawet ręcznie napisana warstwa autoryzacji. Natomiast w środowisku multi-tenant z partnerami, delegacją uprawnień, SSO z domenami klientów – rozwiązania typu Keycloak lub zewnętrzny IdP z bogatym IAM stają się niemal koniecznością.

Jeśli nie masz spisanych wymagań dotyczących ról i przepływów użytkowników (rejestracja, aktywacja, reset hasła, zmiana e-maila, blokada konta), każdy wybór biblioteki będzie w praktyce losowy. Te procesy determinują, gdzie powinna leżeć logika: w aplikacji (Auth.js + własny kod), czy w centralnym serwerze tożsamości (Keycloak, IdP).

Minimalny zestaw wymogów bezpieczeństwa i SSO

Niezależnie od wyboru narzędzia, minimum bezpieczeństwa jest zaskakująco podobne w większości systemów. Obejmuje co najmniej:

  • MFA (Multi-Factor Authentication) lub przynajmniej gotową możliwość jego późniejszego włączenia.
  • Polityki blokady konta po serii nieudanych logowań.
  • Bezpieczny reset hasła w oparciu o jednorazowe tokeny i ograniczenia czasowe.
  • Logowanie zdarzeń – logowania, nieudane próby, zmiany hasła, zmiany uprawnień.
  • Wygaszanie sesji i tokenów po określonym czasie bezczynności / maksymalnym czasie życia.

Punkt kontrolny: czy planowane rozwiązanie (Auth.js, Keycloak lub własny OAuth2) pozwala spełnić te wymagania bez kreatywnej rzeźby. Keycloak ma większość z nich „w pudełku”. Auth.js część rzeczy ułatwia, ale wiele zostawia po stronie aplikacji. Goły OAuth2 implementation bez dodatkowego IAM często nie wspiera takich polityk sam z siebie – to tylko protokół.

Jeżeli odpowiedzi na powyższe pytania są niejasne lub rozproszone po głowach programistów, a nie w jednym dokumencie, każda decyzja technologiczna będzie seriami prób i błędów. Doprecyzowanie wymagań biznesowych, architektonicznych i bezpieczeństwa to nie ozdobnik – to warunek, by porównanie Auth.js, Keycloak i własnego OAuth2 miało sens.

Modele uwierzytelniania i autoryzacji – co naprawdę trzeba rozróżnić

Uwierzytelnianie vs autoryzacja vs zarządzanie tożsamością

Drugi obszar audytu to porządek pojęciowy. Zbyt wiele projektów wchodzi w implementację SSO czy OAuth2, mieszając po drodze uwierzytelnianie, autoryzację i zarządzanie tożsamością. Rezultat: serwer OAuth2 robi pół IAM, aplikacja dokleja resztę, a żadna warstwa nie jest w pełni odpowiedzialna za cokolwiek.

Minimum definicyjne, które trzeba mieć w głowie:

  • Uwierzytelnianie (authentication) – potwierdzenie, że użytkownik jest tym, za kogo się podaje (login/hasło, SSO, MFA).
  • Autoryzacja (authorization) – decyzja, do czego uwierzytelniony użytkownik ma dostęp (rola, uprawnienia, atrybuty).
  • Zarządzanie tożsamością (IAM) – pełny cykl życia użytkownika: tworzenie, zmiana, blokada, usunięcie, delegacja uprawnień, integracja z innymi źródłami tożsamości.
  • IdP (Identity Provider) – system, który uwierzytelnia i wystawia informacje o tożsamości (Keycloak, Azure AD, Auth0).
  • SP / Client – aplikacja korzystająca z tożsamości dostarczonej przez IdP.

Sygnał ostrzegawczy: jeśli w dokumentacji technicznej widzisz zdania typu „OAuth2 służy do logowania użytkownika”, to masz już pierwszy błąd koncepcyjny. OAuth2 jest protokołem autoryzacji dostępu do zasobów, a do logowania ludzi używa się OIDC (OpenID Connect) zbudowanego na bazie OAuth2. Te detale przekładają się potem na strukturę systemu, obsługę claims w tokenach czy zgodność z IdP klasy enterprise.

Jeżeli nie ma jasnego rozdziału „kto za co odpowiada” (IdP, aplikacja, biblioteka w kodzie), system ewoluuje w kierunku trudnego do utrzymania miksu. Centralny punkt kontrolny powinien opisywać: gdzie następuje uwierzytelnianie (IdP), gdzie trzymane są role i atrybuty (IAM vs aplikacja), które decyzje autoryzacyjne podejmuje aplikacja, a które można delegować do polityk w IdP.

Sesje, tokeny i bezstanowość w praktyce

Drugi kluczowy obszar to rozumienie modelu sesji. Spór „sesja vs JWT” z internetu jest w praktyce kompletnie jałowy, jeśli nie wiemy, w jakim kontekście operujemy. Prawidłowe pytanie brzmi: gdzie trzymamy informacje o zalogowaniu i jak je unieważniamy.

Najpopularniejsze podejścia to:

  • Sesja na cookie (ID sesji + serwerowy store) – klasyczne, bezpieczne rozwiązanie dla SSR/monolitów. Cookie HTTP-only wskazuje na rekord na serwerze, gdzie trzymane są dane użytkownika. Łatwo unieważnić sesję, bo wystarczy usunąć wpis z magazynu sesji.
  • Token JWT w kliencie – aplikacja przechowuje w pamięci lub storage token podpisany przez serwer. Serwis backendowy weryfikuje jego podpis i datę ważności. Dobre dla API, gorzej dla bezpieczeństwa frontu, jeśli przechowywane w localStorage.
  • Opaque token + introspekcja – zamiast JWT mamy losowy identyfikator, którego znaczenie zna tylko serwer autoryzacji. Serwisy backendowe wysyłają token do endpointu introspekcji, aby dostać informacje o użytkowniku i uprawnieniach.

W praktyce sensownie jest:

  • używać sesji na cookie HTTP-only tam, gdzie mamy klasyczny backend/SSR i nie potrzebujemy skomplikowanego rozproszenia;
  • używać JWT/opaque tokenów do komunikacji między usługami i aplikacjami (mikroserwisy, API, aplikacje mobilne).

Sygnał ostrzegawczy: ślepe zastępowanie sesji JWT „bo tak się teraz robi”. JWT są trudniejsze do unieważniania przed upływem ważności, podatniejsze na błędy w przechowywaniu po stronie klienta i często wykorzystywane niezgodnie z przeznaczeniem. Jeżeli aplikacja jest głównie SSR, a jedynym powodem wprowadzenia JWT jest moda, warto wrócić do klasycznej, twardej sesji.

W kontekście Auth.js – biblioteka domyślnie opiera się często na sesjach (z tokenami w bazie, mapowanymi do cookie), co jest rozsądne dla pojedynczej aplikacji. Keycloak z kolei operuje tokenami OIDC/OAuth2, a po stronie aplikacji możesz znów używać klasycznych sesji jako lokalnego mechanizmu, mapując token na sesję. Kto nie odróżnia tych poziomów, szybko kończy z plątaniną kilku rodzajów tokenów i sesji, bez planu ich rotacji.

Rozdział odpowiedzialności między IdP, biblioteką a aplikacją

Ostatni element porządkowania modeli to jasne przypisanie ról. Punkt kontrolny przy każdym projekcie uwierzytelniania powinien zawierać odpowiedzi:

  • Kto uwierzytelnia użytkownika (pobiera hasło, weryfikuje MFA)?
  • Gdzie przechowywana jest tożsamość (użytkownicy, hashe haseł, powiązane konta zewnętrzne)?
  • Kto zarządza rolami i uprawnieniami – aplikacja, IdP, system IAM?
  • Gdzie są logi bezpieczeństwa – w aplikacji, IdP, centralnym systemie SIEM?

Granice odpowiedzialności a dobór narzędzia

Jeżeli odpowiedzi na pytania o odpowiedzialność są rozmyte, wybór biblioteki lub serwera tożsamości staje się przypadkowy. Przydatny jest prosty podział:

  • IdP / serwer tożsamości – logowanie, MFA, polityki haseł, federacja z zewnętrznymi dostawcami (Google, Azure AD), centralny audit log logowań.
  • Warstwa autoryzacji – mapowanie użytkownika na role / uprawnienia, interpretacja claims, egzekwowanie decyzji dostępowych (czasem w IdP, czasem w aplikacji lub osobnym serwisie policy).
  • Aplikacja biznesowa – egzekwowanie reguł domenowych, np. „użytkownik może edytować tylko swoje dokumenty”, „admin może nadawać role tylko w obrębie swojego tenant’a”.
  • Biblioteka w kodzie (Auth.js, SDK IdP) – techniczne spięcie protokołów (OIDC/OAuth2), obsługa callbacków, odświeżanie tokenów, magazyn sesji.

Punkt kontrolny: jeśli w Twojej architekturze „aplikacja sama wszystko robi”, a IdP jest tylko źródłem loginu, to nie korzystasz z połowy możliwości serwera tożsamości. Jeśli z kolei cała autoryzacja jest w IdP, a aplikacja nie ma własnych reguł, szybko dojdziesz do ściany przy skomplikowanych zależnościach domenowych.

Jeżeli aplikacja jest prostym panelem, wystarczy często: IdP (lub nawet baza użytkowników) + biblioteka logowania + autoryzacja twardo w kodzie. Gdy mówimy o środowisku z kilkoma aplikacjami, partnerami zewnętrznymi i centralnym audytem, granice odpowiedzialności powinny być spisane i przypisane konkretnym komponentom, inaczej każdy zespół zbuduje własny „mini-IdP”.

Białe klawisze z napisem PASSWORD na koralowym tle jako symbol bezpieczeństwa
Źródło: Pexels | Autor: Miguel Á. Padriñán

Auth.js – kiedy prostota w aplikacji frontendowej ma sens

Profil narzędzia i typowe zastosowania

Auth.js (dawniej next-auth) jest przede wszystkim biblioteką dla aplikacji webowych, ze szczególnym ukłonem w stronę Next.js. Łączy kilka ról: obsługę sesji użytkownika, integrację z zewnętrznymi providerami (Google, GitHub, Azure AD itd.) oraz minimalne API do logowania i wylogowania.

Auth.js sprawdza się, gdy spełnione są następujące warunki:

  • aplikacja jest pojedynczym frontem SSR/SPA lub kilkoma małymi frontami, bez rozbudowanego ekosystemu usług;
  • nie ma potrzeby centralnego zarządzania tożsamością ponad jedną aplikację;
  • role/uprawnienia są stosunkowo proste i mogą być trzymane w bazie aplikacji;
  • główne źródło tożsamości to lokalne konto + kilku providerów social / enterprise, bez złożonej federacji;
  • zespół chce mieć bezpośrednią kontrolę nad UX logowania i logiką rejestracji w kodzie aplikacji.

Sygnał ostrzegawczy: próba uczynienia z Auth.js „lokalnego Keycloak’a” – dopinanie paneli administracyjnych, zewnętrznych provisioningów, zaawansowanych przepływów SSO – kończy się zwykle rozlanym kodem i trudnym do utrzymania zestawem endpointów.

Jeżeli aplikacja to jeden portal B2B z kilkoma rolami, Auth.js pozwala szybko i względnie bezpiecznie uruchomić logowanie, bez budowy lub wdrażania osobnego IdP. Jeśli jednak mówisz o ekosystemie kilku aplikacji i centralnym SSO, Auth.js nie jest zamiennikiem serwera tożsamości – jest tylko biblioteką w jednym z klientów.

Modele sesji i integracja z backendem

Auth.js domyślnie opiera się na sesji zapisywanej w bazie i identyfikowanej przez cookie HTTP-only. To sensowny kompromis między bezpieczeństwem a prostotą:

  • przeglądarka przechowuje tylko losowe ID sesji, brak JWT po stronie klienta, mniejsza ekspozycja na XSS;
  • unieważnienie sesji jest trywialne – wystarczy usunąć rekord z bazy lub oznaczyć jako wygaszony;
  • możesz rozszerzyć model sesji o własne pola (np. ID tenant’a, wersję regulaminu zaakceptowaną przez użytkownika).

Kluczowy punkt projektowy dotyczy integracji z backendami (mikroserwisy, API). Typowe opcje to:

  • backend SSR (Next.js) sam wykonuje logikę biznesową – wówczas Auth.js wystarcza jako jedyny punkt autoryzacji;
  • backend API za reverse proxy – Auth.js uwierzytelnia użytkownika, a proxy (np. nginx) dokleja nagłówki z ID użytkownika/rolą do wywołań API;
  • oddzielne API – Auth.js ekspozytuje endpoint, który mapuje sesję na token (JWT/opaque), wykorzystywany dalej przez API.

Punkt kontrolny: trzeba zdecydować, czy API ufa sesji Auth.js bezpośrednio (ta sama domena i warstwa), czy potrzebuje odrębnego tokenu serwisowego. Mieszanie obu podejść bez jasnego modelu prowadzi do niespójnych decyzji – część endpointów ufa cookie, część wymaga JWT.

Jeśli większość logiki żyje w jednym serwerze Next.js, wbudowany model sesji Auth.js jest zwykle wystarczający. Gdy jednak powstaje prawdziwe API używane przez kilka klientów (web, mobile, partnerzy), potrzebny jest dodatkowy projekt przepływu tokenów, a Auth.js pełni rolę tylko pierwszego kroku uwierzytelnienia.

Obsługa providerów zewnętrznych i pułapki konfiguracji

Jedna z głównych zalet Auth.js to gotowe integracje z dziesiątkami providerów. Dopinając Google, GitHub czy Azure AD, otrzymujesz gotowe ekrany logowania i przepływy OAuth/OIDC. To jednak niesie kilka praktycznych pułapek:

  • często brakuje spójnego modelu użytkownika – każdy provider mapowany jest inaczej, co utrudnia zachowanie jednej tożsamości przy wielu logowaniach;
  • konfiguracja scopes i claims bywa trywializowana – z czasem okazuje się, że aplikacja potrzebuje dodatkowych atrybutów, których nie przewidziano na starcie;
  • brak centralnego panelu IAM – uprawnienia i role są powiązane z kontem w Twojej bazie, a nie zewnętrznym IdP, co przy integracjach enterprise ogranicza elastyczność.

Sygnał ostrzegawczy: mapowanie roli biznesowej bezpośrednio z groups czy roles z Azure AD/Keycloak do lokalnej roli, bez warstwy pośredniej, utrudnia późniejsze zmiany modelu. Przy pierwszym spięciu z dużym klientem korporacyjnym to zazwyczaj wychodzi na jaw.

Jeżeli Auth.js ma integrować się z IdP klienta enterprise, najbezpieczniej jest traktować go jako warstwę techniczną OIDC, a model ról i uprawnień trzymać po swojej stronie, w oparciu o claims otrzymane z IdP. Jeśli potrzebujesz panelu do edycji ról z poziomu działu bezpieczeństwa klienta, Auth.js będzie za cienki – tam wchodzą w grę klasyczne IdP i IAM.

Bezpieczeństwo w praktyce: co sprawdzić przy Auth.js

Minimalny audyt wdrożenia Auth.js powinien obejmować:

  • czy cookie sesyjne jest HTTP-only, Secure, SameSite ustawione adekwatnie do modelu (Lax/Strict/None);
  • czy parametry callback URLs są ściśle zdefiniowane, bez otwartych redirectów (brak wildcardów typu * na środowisku produkcyjnym);
  • czy sekrety i klucze (JWT secret, klucze providerów) są przechowywane poza repozytorium (secret manager, KMS);
  • czy istnieje polityka wylogowania globalnego – co się dzieje z sesją po zmianie hasła, odebraniu uprawnień, blokadzie konta.

Punkt kontrolny: jeśli jakikolwiek członek zespołu potrafi wstrzyknąć własny redirect URL lub odtworzyć sesję tylko na podstawie ID sesji z logów, wdrożenie wymaga korekty. Tu Auth.js nie jest ani bardziej, ani mniej bezpieczny niż inne biblioteki – większość ryzyk wynika z konfiguracji.

Jeżeli po takim audycie większość punktów świeci się na zielono, Auth.js jest rozsądną opcją dla małej i średniej aplikacji webowej. Jeśli jednak połowa wymagań krzyczy o centralnym zarządzaniu tożsamością, MFA politycznym i federacji, nie ma sensu naciągać tej biblioteki do roli, której nie pełni.

Keycloak – kiedy potrzebny jest pełnoprawny serwer tożsamości

Typowe scenariusze zastosowania

Keycloak to serwer tożsamości i IAM, a nie biblioteka. Zaprojektowany do obsługi:

  • wielu aplikacji (web, mobilnych, API) korzystających z jednego SSO;
  • wielu realmów (tenantów) z odrębną konfiguracją polityk bezpieczeństwa;
  • federacji z zewnętrznymi katalogami (LDAP, Active Directory) i IdP (SAML, OIDC);
  • zaawansowanych polityk haseł, MFA, consentów, fine-grained role/permissions.

Jeżeli system docelowo będzie obejmował kilka aplikacji, klientów B2B, centralne logowanie i odłączanie użytkowników, Keycloak jest jednym z niewielu projektów open-source, które to zapewniają „od ręki”. Próba replikowania tego w Auth.js lub własnym OAuth2 zwykle kończy się latami dłubania.

Jeśli założenie jest takie, że za rok pojawi się druga aplikacja, integracja z portalem partnera i potrzeba centralnego audytu logowań, projektowanie z myślą o serwerze tożsamości (Keycloak lub inny IdP) jest bezpieczniejszą ścieżką niż „zobaczymy później”.

Realmy, klienci i role – porządek modeli

Keycloak wprowadza własny słownik pojęć, który trzeba uporządkować, zanim zacznie się go wykorzystywać:

  • Realm – izolowany przestrzeń tożsamości (coś w rodzaju „domeny bezpieczeństwa”). Użytkownik nie istnieje poza swoim realmem.
  • Client – aplikacja lub usługa, która używa Keycloak’a (OIDC lub SAML). Każdy client ma własną konfigurację redirectów, secretów i polityk.
  • Client roles – role przypisane w kontekście konkretnego clienta.
  • Realm roles – globalne role w obrębie całego realmu.
  • Groups – grupowanie użytkowników, z możliwością przypisania ról grupie.

Sygnał ostrzegawczy: bez decyzji na poziomie architektury, czy Twoje role biznesowe mają siedzieć jako realm roles, client roles czy w aplikacji, bardzo łatwo zbudować chaotyczny miks. Utrudnia to migracje, audyty i integracje z zewnętrznymi systemami.

Punkt kontrolny: trzeba zdefiniować model mapowania – np. role techniczne (dostęp do API X) jako client roles, a role biznesowe (admin tenant’a, pracownik działu X) jako realm roles albo profile w aplikacji. Zmiana tego modelu po roku produkcji bywa bolesna.

Jeśli zespół świadomie ustali, które role są „techniczne” (używane przez API do decydowania, które endpointy są dostępne), a które „biznesowe” (logika domeny), Keycloak może odciążyć aplikacje z dużej części IAM. W przeciwnym przypadku stanie się tylko kolejnym katalogiem, który „gdzieś ma role, ale nie wiadomo, które są ważne”.

Przepływy logowania i SSO

Keycloak oferuje konfigurowalne flow logowania, w tym obsługę MFA, sprawdzanie warunków (np. pierwsze logowanie, wymuszona zmiana hasła) czy integrację z zewnętrznymi IdP. Z punktu widzenia architekta to oznacza:

  • możliwość centralnego włączania/wyłączania MFA dla całego realmu lub wybranych użytkowników;
  • konfigurację różnych identity providerów (np. SAML do intranetu, OIDC do Google) w jednym miejscu;
  • zarządzanie sesją SSO – jedna sesja w Keycloak może obsługiwać wiele aplikacji, a globalne wylogowanie zamyka wszystkie powiązane sesje.

Sygnał ostrzegawczy: próba „przepisania ekranów logowania po swojemu” bez zrozumienia flow w Keycloak często kończy się łamaniem standardów OIDC, mieszaniem własnych cookie z sesją IdP i utratą części korzyści z SSO. Jeśli UX logowania musi być w pełni customowy, trzeba zrobić to przez oficjalnie wspierane mechanizmy (themes, custom authenticators), a nie obok.

Jeżeli kryterium jest globalne SSO w ekosystemie kilku aplikacji – jeden login do portalu klienta, panelu administracyjnego, API partnera – Keycloak jest naturalnym kandydatem. Gdy jednak masz pojedynczą aplikację bez planów rozwoju ekosystemu, SSO na poziomie IdP może być przerostem formy nad treścią.

Bezpieczeństwo, polityki i audyt

Keycloak wyróżnia się tym, że wiele polityk bezpieczeństwa jest dostępnych „z pudełka”:

  • polityki haseł (złożoność, długość, historia, wygaśnięcie);
  • blokady konta po błędnych logowaniach;
  • logi audytowe – logowania, reset haseł, zmiany ról, tworzenie/usuńcie kont;
  • fine-grained admin permissions – ograniczenie tego, co może zrobić dany administrator realm’u lub klienta.

Pułapki wdrożenia i utrzymania Keycloak

Keycloak rozwiązuje wiele problemów, ale wnosi własne ryzyka operacyjne. Najczęściej pomijane są trzy obszary: utrzymanie klastra, upgrade’y oraz granica odpowiedzialności między zespołami.

  • Utrzymanie – pojedynczy Keycloak „na VM-ce” działa imponująco prosto, dopóki nie pojawi się wymóg wysokiej dostępności, kopii zapasowych i monitoringu. Klaster wymaga bazy, health-checków, sensownej strategii backup/restore i testów disaster recovery.
  • Upgrade’y – zmiany wersji potrafią wpływać na schemat bazy danych, protokoły i zachowanie adapterów. Brak środowiska testowego z danymi zbliżonymi do produkcji i automatycznych smoke-testów logowania to zaproszenie do awarii na produkcji.
  • Granica odpowiedzialności – jeśli bezpieczeństwem zarządza osobny zespół, a aplikację rozwija inny, konflikty na linii „kto może zmieniać realmy, role, clienty” są kwestią czasu. Bez jasno opisanych ról operacyjnych panuje chaos.

Sygnał ostrzegawczy: jeśli nikt w organizacji nie czuje się właścicielem utrzymania Keycloak (monitoring, backupy, upgrade’y), w praktyce jest to kolejny krytyczny system bez opiekuna. Przy pierwszym poważniejszym incydencie lub awarii zostanie to szybko „rozliczone”.

Punkt kontrolny: przed wdrożeniem produkcyjnym powinien istnieć runbook operacyjny – jak dodać nowy realm, jak przywrócić bazę po awarii, jak aktualizować wersję, jak odcinać użytkowników w trybie awaryjnym. Jeżeli taki dokument istnieje, a zespół przeszedł przez symulację awarii na testach, Keycloak przestaje być czarną skrzynką.

Jeśli celem jest centralne IAM z audytem i HA, a organizacja może utrzymywać kolejny krytyczny komponent, Keycloak jest uzasadnionym wyborem. Jeżeli jednak nie ma zespołu, który może wziąć na siebie jego eksploatację, lepiej rozważyć zarządzane IdP lub prostszy model oparty o Auth.js i zewnętrznego providera.

Współistnienie Keycloak i bibliotek w aplikacji

Keycloak nie wyklucza stosowania Auth.js czy innych bibliotek – często działa w parze z nimi. Typowy wzorzec to:

  • Keycloak jako centralne IdP i IAM (SSO, MFA, polityki, federacja),
  • Auth.js lub inne biblioteki jako klienci OIDC integrujący front i backend z Keycloak.

Największe błędy wynikają z podwójnego modelowania tych samych bytów: ról, sesji, atrybutów użytkownika. Jeśli aplikacja ma własne role oraz Keycloak ma własne role, a nie ma jednoznacznego mapowania, regresje w autoryzacji są niemal gwarantowane.

Sygnał ostrzegawczy: dublowanie sesji – aplikacja utrzymuje swoją „sesję” i równoległą sesję w Keycloak, bez spójnej polityki wygasania i wylogowania. Użytkownik jest wylogowany z Keycloak, ale dalej działa mu cookie w aplikacji lub odwrotnie.

Punkt kontrolny: na poziomie architektury trzeba zdefiniować, co jest źródłem prawdy dla: tożsamości użytkownika, sesji SSO oraz ról. Przykładowy kompromis: Keycloak odpowiada za tożsamość i SSO, natomiast role biznesowe trzymane są w aplikacji i są nadawane na podstawie atrybutów (claims) z Keycloak oraz lokalnych konfiguracji.

Jeśli Keycloak ma pełnić rolę centralnego IdP, a aplikacje są przygotowane na korzystanie z niego jako „jednego źródła prawdy”, duet Keycloak + biblioteki klienckie działa przewidywalnie. Gdy jednak każda aplikacja zacznie „poprawiać” model ról i sesji po swojemu, centralny IAM stanie się fasadą, a realne decyzje i tak będą rozproszone.

Zbliżenie klawiszy klawiatury z napisem login na koralowym tle
Źródło: Pexels | Autor: Miguel Á. Padriñán

„Czysty” OAuth2 i OIDC – kiedy budowanie własnego rozwiązania ma sens

Co to znaczy „czysty” OAuth2 w praktyce

„Czysty” OAuth2/OIDC oznacza, że korzystasz bezpośrednio z komponentów protokołu (autoryzacja, tokeny, introspekcja, userinfo), a warstwę UX, sesji i integracji budujesz samodzielnie. Zwykle wygląda to tak:

  • masz własny backend, który pełni rolę authorization server lub korzystasz z prostego serwera open-source (np. bazującego na bibliotekach implementujących RFC);
  • front i inne usługi komunikują się z tym serwerem przy użyciu standardowych przepływów (Authorization Code + PKCE, Client Credentials itd.);
  • ekrany logowania, reset hasła, profile użytkownika i panele administracyjne budujesz „ręcznie” nad tym mechanizmem.

Ten model daje maksymalną kontrolę, ale oznacza też przejęcie odpowiedzialności za poprawność protokołu, bezpieczeństwo oraz migracje. Zaletą jest precyzyjne dopasowanie do potrzeb domenowych. Wadą – wieloletnie utrzymanie bardzo newralgicznego fragmentu systemu.

Sygnał ostrzegawczy: próba „nauczenia się OAuth2” poprzez pisanie własnego authorization serwera w pierwszym produkcyjnym projekcie. Na poziomie POC da się to utrzymać, ale w środowiskach produkcyjnych drobne błędy w przepływach lub walidacji tokenów kończą się incydentami bezpieczeństwa.

Punkt kontrolny: jeśli w zespole nie ma osoby, która swobodnie operuje pojęciami z RFC (authorization grant, PKCE, nonce, state, token binding) i potrafi je przełożyć na kod i konfigurację, budowanie własnego IdP jest zbyt ryzykowne.

Jeżeli jednak produkt wymaga niestandardowych przepływów, integracji low-level lub ma specyficzne ograniczenia (np. środowiska on-premise bez możliwości użycia chmurowego IdP), „czysty” OAuth2 może być racjonalnym wyborem – pod warunkiem, że jest to świadoma decyzja, a nie efekt braku rozeznania w istniejących narzędziach.

Typowe powody, dla których zespoły i tak piszą własny IdP

Nawet mając Auth.js czy Keycloak, zespoły często lądują z autorskim „mini-IdP”. Powody są zwykle podobne:

  • nietypowe modele użytkowników – np. tożsamości krótkotrwałe, „użytkownicy systemowi”, konta wiązane z urządzeniami a nie osobami, gdzie klasyczne IdP są zbyt sztywne;
  • wymogi regulacyjne lub wewnętrzne polityki zabraniające użycia zewnętrznego IdP albo narzucające specyficzne formaty logów, audytu czy kryptografii;
  • legacy – istniejący monolit już realizuje logowanie i sesje; próba migrowania wszystkiego na IdP w krótkim czasie jest nierealna, więc rozbudowuje się istniejące rozwiązanie o warstwę OAuth2/OIDC;
  • wydajność i kontrola – system o bardzo dużym obciążeniu i niestandardowych wymaganiach co do czasu życia tokenów, cache’owania, kryptografii, gdzie gotowe produkty są zbyt ciężkie lub trudne do dostosowania.

Sygnał ostrzegawczy: argument „Keycloak jest za ciężki, więc napisaliśmy własny authorization server” bez specyficznego uzasadnienia technicznego lub biznesowego. W większości przypadków „za ciężki” oznacza brak chęci zrozumienia modelu lub jego odpowiedniej konfiguracji.

Punkt kontrolny: jeśli głównym motywem jest „mieć pełną kontrolę nad kodem”, trzeba od razu dopisać do backlogu: coroczne przeglądy pod kątem zmian w standardach, aktualizacje kryptografii, testy penetracyjne i dedykowany monitoring tego komponentu. W przeciwnym razie „pełna kontrola” zamieni się w „pełną odpowiedzialność za techniczny dług”.

Jeżeli istnieje realna potrzeba odstępstw od standardowych rozwiązań (np. przepływy nieobsługiwane przez popularne serwery OAuth2, szczególne wymagania audytowe), budowa własnego IdP może być uzasadniona. Gdy powodem jest tylko wygoda lub niechęć do nauki istniejących narzędzi, to sygnał, że decyzję warto zatrzymać i przeanalizować ponownie.

Minimalny zakres, jaki trzeba ogarnąć przy własnym OAuth2/OIDC

Jeśli mimo wszystko zapada decyzja o własnym rozwiązaniu, istnieje minimalny zestaw elementów, który musi być zaadresowany, żeby nie stworzyć atrapowego bezpieczeństwa:

  • poprawna implementacja przepływów: Authorization Code z PKCE dla aplikacji publicznych, Client Credentials dla machine-to-machine; brak dopuszczenia implicit flow dla SPA w nowych wdrożeniach;
  • zarządzanie tokenami: czas życia access/refresh tokenów, rotacja refresh tokenów, mechanizm unieważniania (revocation), ewentualnie introspekcja tokenów dla serwisów backendowych;
  • kryptografia: generowanie i rotacja kluczy (JWKS), silne algorytmy (preferowane algorytmy z rodziny ES/RS, unikanie HS256 jako domyślnego, jeśli klucze muszą być współdzielone), poprawna walidacja podpisu i audience/issuer/nonce;
  • bezpieczne przechowywanie sekretów: brak secretów w repozytorium, integracja z KMS/secret managerem, polityka rotacji i audyt użycia;
  • obsługa błędów: zgodne ze standardem kody błędów OAuth2/OIDC, brak wycieków szczegółów technicznych w odpowiedziach dla klienta, spójne logowanie incydentów;
  • obsługa sesji użytkownika: jeśli serwer ma pośredniczyć między przeglądarką a backendami, potrzebny jest spójny model sesji (przeglądarka ↔ authorization server ↔ resource server) z jasnymi zasadami wygasania i globalnego wylogowania.

Sygnał ostrzegawczy: brak zautomatyzowanych testów integracyjnych, które przechodzą pełne ścieżki logowania, odświeżania tokenów, wylogowania i odwołania uprawnień. Ręczne „kliknięcie” happy-path w przeglądarce nie jest testem bezpieczeństwa.

Punkt kontrolny: minimum to oddzielny projekt testowy, który jako klient OIDC/OAuth2 waliduje wszystkie podstawowe przepływy, w tym niepoprawne scenariusze (zły state, nonce, przeterminowane tokeny, podmieniony redirect_uri). Jeśli taki zestaw testów istnieje i jest uruchamiany przy każdej zmianie, ryzyko regresji protokołu znacząco spada.

Jeśli organizacja jest gotowa rozwijać i utrzymywać taki pakiet funkcji przez lata, „czysty” OAuth2 może pełnić rolę fundamentu architektury bezpieczeństwa. Przy braku takiej gotowości lepiej podeprzeć się dojrzałym serwerem OAuth2/OIDC i skupić wysiłek na domenie biznesowej.

Granica między IdP a aplikacją przy własnym rozwiązaniu

Przy samodzielnym wdrożeniu OAuth2/OIDC szczególnie łatwo rozmyć odpowiedzialność. Pojawia się pokusa, by część logiki autoryzacji wypychać do IdP, a część wciągać z powrotem do aplikacji, co w efekcie uniemożliwia sensowną ewolucję obu komponentów.

Praktyczne podejście to zdefiniowanie granicy:

  • IdP (Twój serwer OAuth2/OIDC) odpowiada za tożsamość (kto to jest, czy jest uwierzytelniony, jakie ma globalne atrybuty);
  • aplikacja odpowiada za autoryzację domenową (co ten użytkownik może zrobić w kontekście danego systemu, tenant’a, dokumentu);
  • IdP może udostępniać atrybuty i „surowe” role techniczne, ale ostateczne decyzje uprawnień są podejmowane w kodzie aplikacji lub dedykowanej warstwie autoryzacji.

Sygnał ostrzegawczy: discovery w kodzie, że logika typu „czy użytkownik może wyeksportować raport finansowy za poprzedni rok” jest zapisana jako jedna z ról w JWT generowanym przez IdP. Takie decyzje trudno utrzymać, przetestować i zmienić bez modyfikacji centralnego serwera.

Punkt kontrolny: model atrybutów i ról, które IdP wkłada do tokenu, powinien być ogólny i stabilny (np. typ użytkownika, tenant, zbiór cech), natomiast szczegółowe reguły dostępu powinny leżeć bliżej domeny. Dzięki temu ewolucja logiki biznesowej nie wymaga ciągłego grzebania w infrastrukturowym komponencie.

Jeżeli uda się jasno rozdzielić poziom „kim jesteś i jakie masz cechy” (IdP) od „do czego te cechy cię uprawniają w tej konkretnej aplikacji” (autoryzacja domenowa), własny serwer OAuth2/OIDC staje się stabilnym fundamentem. W przeciwnym razie szybko zamienia się w mieszankę protokołu, logiki biznesowej i konfiguracji, której boi się dotykać cały zespół.

Bezpieczeństwo operacyjne własnego IdP

Własny serwer tożsamości to nie tylko kod. To również procesy i operacje wokół niego. Zaniedbane, szybko neutralizują korzyści z posiadania „idealnie dopasowanego” rozwiązania:

  • monitoring – metryki sukcesów/porażek logowań, czas odpowiedzi, liczba żądań na poszczególnych endpointach OAuth2, anomalie (nagłe skoki prób nieudanych logowań, geolokalizacja, nietypowe klienty);
  • logowanie i audyt – spójne logi audytowe: kto się zalogował, z jakiego adresu, do czego uzyskał token, jakie zmiany wprowadzono w kontach i konfiguracji; logi powinny być nieredagowalne (WORM, SIEM) lub przynajmniej odporne na manipulacje;
  • proces zarządzania incydentami – gotowe procedury: co zrobić, gdy wykryto kradzież tokenów, kompromitację sekretu, nieautoryzowane modyfikacje kont, awarię serwera autoryzacji;
  • regularne testy bezpieczeństwa – testy penetracyjne skupione właśnie na przepływach OAuth2/OIDC, manipulacji parametrami, CSRF, przejęciu sesji i błędach w walidacji tokenów.

Najważniejsze punkty

  • Pierwszy punkt kontrolny to kontekst biznesowy i regulacyjny: rodzaj danych, skala użytkowników, wymogi RODO/PSD2/HIPAA i audytowalność są ważniejsze niż sam wybór narzędzia. Jeśli działasz w silnie regulowanym sektorze, „lekka” biblioteka w aplikacji zwykle nie spełni minimum wymagań.
  • Architektura aplikacji (SSR, SPA, mobilna, mikroserwisy) wprost determinuje klasę rozwiązania do uwierzytelniania. Dla pojedynczej aplikacji SSR wystarczy często Auth.js, natomiast SPA, mobilki i mikroserwisy to sygnał ostrzegawczy, że potrzebny będzie centralny serwer autoryzacji (Keycloak lub inny IdP).
  • Model użytkowników, ról i tenantów trzeba zdefiniować przed wyborem technologii – inaczej decyzja jest losowa. Prosty B2B z kilkoma rolami można obsłużyć lokalnie, lecz w środowisku multi-tenant, z delegacją uprawnień i SSO klientów, konieczne staje się rozwiązanie klasy IAM (Keycloak / enterprise IdP).
  • Auth.js dobrze spełnia rolę „minimum rozsądku” w produktach SaaS o umiarkowanej skali i bez ciężkich regulacji, zwłaszcza w stackach typu Next.js/SSR. Gdy tylko pojawia się potrzeba centralnego zarządzania tożsamością w wielu systemach, Auth.js przestaje wystarczać jako główny filar bezpieczeństwa.
  • Keycloak i podobne IdP są naturalnym wyborem, gdy wymagane są: centralny punkt SSO, rozbudowane polityki bezpieczeństwa (MFA, złożone hasła), integracje z providerami zewnętrznymi (Google, Azure AD, SAML) oraz szczegółowy audyt operacji. Jeśli organizacja wymusza centralne logowanie, lokalne „biblioteki do logowania” to techniczny dług, nie rozwiązanie.
Poprzedni artykułPhishing 2.0: jak rozpoznać fałszywe wiadomości
Następny artykułAI w planowaniu produkcji: lepsze harmonogramy bez chaosu w ERP
Zuzanna Nowak
Zuzanna Nowak pisze o AI, automatyzacji i praktycznych zastosowaniach uczenia maszynowego w biznesie. Łączy perspektywę analityczną z doświadczeniem w pracy z danymi: od przygotowania zbiorów, przez dobór metryk, po ocenę ryzyk wdrożeniowych. W tekstach stawia na weryfikowalne źródła, testy narzędzi na realnych scenariuszach i jasne wyjaśnianie ograniczeń modeli. Interesują ją także etyka, prywatność i wpływ nowych technologii na użytkowników. Na Polskiekino.com.pl dba o to, by rekomendacje były odpowiedzialne i możliwe do odtworzenia.