Jak zintegrować SSO i dwuskładnikowe uwierzytelnianie w aplikacji webowej krok po kroku

0
67
5/5 - (1 vote)

Nawigacja:

Fundamenty: co oznacza SSO i 2FA w praktycznej aplikacji webowej

SSO – pojedyncze logowanie, wiele aplikacji

Single Sign-On (SSO) to mechanizm, w którym użytkownik loguje się raz do dostawcy tożsamości (IdP – Identity Provider), a następnie korzysta z wielu aplikacji (tzw. service provider – SP lub relying party – RP) bez ponownego podawania hasła. Krytyczny element to zaufanie między tymi systemami: aplikacja ufa informacjom, które otrzymuje z IdP w postaci tokenów lub asercji.

W typowym scenariuszu SSO nie przechowujesz ani nie weryfikujesz hasła użytkownika w swojej aplikacji webowej. Przekierowujesz go do IdP (np. Azure AD, Keycloak, Okta), a po poprawnym logowaniu otrzymujesz podpisany kryptograficznie komunikat potwierdzający tożsamość (np. token ID w OIDC albo asercję SAML). Twoja aplikacja musi ten komunikat sprawdzić i na jego podstawie utworzyć sesję użytkownika.

SSO różni się od prostego „Zaloguj przez Google/Facebook” z punktu widzenia odpowiedzialności i zakresu integracji. Logowanie społecznościowe zwykle służy do uwierzytelnienia użytkownika B2C, bez centralnej kontroli polityk bezpieczeństwa w organizacji. Pełne SSO najczęściej jest wdrażane w środowiskach firmowych, gdzie wiele systemów korzysta z jednego IdP, a administracja może zarządzać politykami haseł, 2FA i dostępem z centralnego panelu.

Jeśli w projekcie nadal przechowujesz hasła i próbujesz „dokleić” SSO jako dodatkową opcję logowania, rośnie ryzyko bałaganu: dwa źródła prawdy o tożsamości, zduplikowane konta, niespójne polityki bezpieczeństwa. Pierwszy punkt kontrolny: jasno określ, czy Twoja aplikacja po wdrożeniu SSO ma przestać zarządzać hasłami, czy też utrzymujesz tryb hybrydowy (i na jakich zasadach).

2FA/MFA – drugi składnik i czynniki uwierzytelniania

Dwuskładnikowe uwierzytelnianie (2FA) i wieloskładnikowe uwierzytelnianie (MFA) dodają do procesu logowania kolejny czynnik poza samym hasłem. Chodzi o połączenie co najmniej dwóch rodzajów czynników:

  • Coś, co wiesz – hasło, PIN, odpowiedź na pytanie bezpieczeństwa.
  • Coś, co masz – telefon z aplikacją TOTP (Google Authenticator, Authy), klucz U2F/FIDO2, token sprzętowy, SMS.
  • Coś, czym jesteś – odcisk palca, rozpoznawanie twarzy, głosu.

W praktyce w aplikacjach webowych najczęściej spotykany jest kod TOTP (6-cyfrowy, ważny kilkadziesiąt sekund) w aplikacji mobilnej lub SMS. Nowszym, bezpieczniejszym standardem jest WebAuthn/FIDO2, wykorzystujący klucze sprzętowe lub wbudowane mechanizmy biometryczne systemu operacyjnego. SMS bywa wygodny, ale ma sporo słabości (przejęcie numeru, ataki SIM swap), więc trudno go uznać za docelowy, docelowy standard w systemach o wysokich wymaganiach bezpieczeństwa.

Od strony integracji minimum rozsądku to takie zaprojektowanie procesu, aby 2FA było wykonywane po stronie IdP, a nie jako samodzielny, „domowy” mechanizm w Twojej aplikacji. Dzięki temu:

  • nie musisz przechowywać sekretów TOTP ani budować własnej logiki weryfikacji kodów,
  • centralnie definiujesz polityki (kto musi używać 2FA, jakie metody są dopuszczalne),
  • logi zdarzeń bezpieczeństwa są zebrane w jednym miejscu.

Jeżeli 2FA jest traktowane wyłącznie jako „opcjonalny gadżet” i nie jest konsekwentnie egzekwowane tam, gdzie powinno, wtedy SSO jedynie upraszcza życie użytkownikom – ale nie podnosi znacząco poziomu bezpieczeństwa. Połączenie SSO + 2FA ma sens dopiero wtedy, gdy druga warstwa jest wymuszona politykami IdP dla odpowiednich grup użytkowników i aplikacji.

Ścieżka użytkownika w SSO z 2FA – obraz krok po kroku

Przykładowy przebieg logowania przy użyciu SSO oraz 2FA (OIDC, authorization code flow):

  1. Użytkownik wchodzi na stronę Twojej aplikacji.
  2. Aplikacja sprawdza, czy jest aktywna sesja użytkownika. Jeśli nie – generuje żądanie autoryzacji do IdP i wykonuje przekierowanie (HTTP 302) do endpointu /authorize IdP.
  3. IdP sprawdza, czy użytkownik ma już ważną sesję SSO (np. z wcześniej otwartej innej aplikacji). Jeśli nie – wyświetla formularz logowania (login/hasło).
  4. Po wprowadzeniu poprawnych danych IdP ocenia politykę 2FA. Jeśli dana aplikacja lub użytkownik wymaga drugiego składnika, IdP:
    • wysyła SMS,
    • oczekuje kodu TOTP,
    • wywołuje proces WebAuthn.
  5. Po poprawnym 2FA IdP tworzy sesję SSO (cookie domenowe IdP) oraz przekierowuje użytkownika z powrotem do Twojej aplikacji z kodem autoryzacyjnym.
  6. Backend Twojej aplikacji odbiera kod, wysyła żądanie /token do IdP (z dodanym PKCE verifier), otrzymuje tokeny (access token, id token, opcjonalnie refresh token).
  7. Backend weryfikuje poprawność tokenów (sygnatura, aud, iss, exp, nonce) i na tej podstawie zakłada lub odświeża sesję aplikacyjną użytkownika (cookie httpOnly, secure).

Od tego momentu użytkownik działa w Twojej aplikacji, a 2FA jest zrealizowane w sposób centralny. Jeśli kolejna aplikacja w tym samym środowisku używa tego samego IdP, user już nie zobaczy logowania ani 2FA – będzie korzystał z SSO, o ile polityka nie wymusi ponownego uwierzytelnienia (np. dla krytycznych operacji).

Jeżeli ten przepływ nie jest zrozumiany i narysowany (choćby na tablicy), konfiguracja redirect_uri, sesji i tokenów zwykle kończy się chaosem. Jeśli zespołowi trudno opisać krok po kroku powyższą ścieżkę, sygnał ostrzegawczy jest jasny: integracja SSO z 2FA będzie ryzykowna bez dodatkowego dopracowania koncepcji.

Wybór protokołu i dostawcy tożsamości – decyzje strategiczne

OAuth2 + OpenID Connect vs SAML vs własne SSO

Podstawą integracji SSO w aplikacji webowej jest wybór protokołu. W praktyce najczęściej stosowane są trzy opcje:

  • OAuth 2.0 + OpenID Connect (OIDC) – nowoczesny standard oparty na JSON i REST.
  • SAML 2.0 – starszy, mocno zakorzeniony w środowiskach korporacyjnych, oparty na XML.
  • Autorski, „własny” mechanizm SSO – tworzenie czegoś od zera, zazwyczaj poza standardami.

OAuth2 jest w swojej naturze protokołem autoryzacji, a nie uwierzytelniania. Do logowania użytkownika służy rozszerzenie OpenID Connect, które dodaje token ID i jasno zdefiniowane endpointy oraz scope’y (openid, profile, email itd.). Z punktu widzenia aplikacji webowej OIDC jest dziś w większości przypadków minimum sensownym wyborem – ma bogate wsparcie bibliotek, dobre praktyki (PKCE, nonce), jest wspierany przez większość współczesnych IdP.

SAML był standardem de facto w erze systemów korporacyjnych on-premise. Jeśli głównym celem jest integracja z dużym klientem korporacyjnym lub z istniejącą infrastrukturą (np. stare systemy HR, portale B2B), może być jedyną realną opcją. Wymaga jednak oswojenia z XML-ami, podpisami XML i bardziej skomplikowaną konfiguracją metadanych.

Własne SSO – np. „mamy JWT podpisane prywatnym kluczem, więc to już SSO” – to częsty sygnał ostrzegawczy. Brak standaryzacji oznacza brak sprawdzonych bibliotek, niższą interoperacyjność i większe ryzyko błędów bezpieczeństwa (np. brak rotacji kluczy, błędna walidacja audience, brak nonce). Jeśli nie ma bardzo silnego, uzasadnionego biznesowo powodu, aby budować własne SSO, bezpieczniejsze i tańsze w utrzymaniu będzie użycie OIDC lub SAML.

Jeśli aplikacja ma być rozwijana w modelu B2C, mobilnym lub jako SPA, dominującym wyborem pozostaje OIDC z odpowiednim przepływem OAuth2. Jeśli docelowi klienci to głównie duże korporacje z istniejącymi IdP działającymi w SAML – trzeba przygotować się na obsługę obu standardów albo na integrację pośrednią przez bramki (np. Keycloak jako broker między SAML a OIDC).

Różnica ról: Authorization Server, Identity Provider, Resource Server

Dla porządku warto wyraźnie odróżnić kluczowe role w ekosystemie OAuth2/OIDC:

  • Authorization Server / Identity Provider – system, który uwierzytelnia użytkownika, wydaje tokeny (access token, id token) i egzekwuje polityki (hasła, 2FA, blokady kont). To tu integrujesz 2FA/MFA.
  • Client / Relying Party – Twoja aplikacja webowa, która kieruje użytkownika do IdP, a potem odbiera i weryfikuje tokeny.
  • Resource Server / API – usługa, która przyjmuje access token i na tej podstawie decyduje, czy request ma dostęp do określonych zasobów.

Częsty błąd polega na pomieszaniu tych ról: aplikacja próbuje sama być IdP (zarządzać hasłami, 2FA, wydawać tokeny), a równocześnie pełnić rolę klienta innych IdP. W niewielkich projektach może to „jakoś działać”, ale skalowanie i audyt bezpieczeństwa stają się koszmarem.

Jeśli w projekcie rola IdP i Resource Server jest w jednym monolicie, warto przynajmniej logicznie to oddzielić i mieć jasny model: który komponent odpowiada za tożsamość i login, a który tylko przyjmuje tokeny i autoryzuje dostęp do zasobów. Brak takiego podziału to punkt kontrolny – przed integracją SSO z 2FA należy zidentyfikować i rozdzielić odpowiedzialności.

Kryteria wyboru dostawcy tożsamości (Keycloak, Azure AD, Auth0, Okta, własny)

Wybierając IdP do integracji SSO i 2FA, dobrze jest przejść przez checklistę kryteriów. Dla aplikacji webowej sensownym minimum jest:

  • Wsparcie dla OIDC i OAuth2 – w szczególności authorization code flow z PKCE.
  • Natywne wsparcie MFA – TOTP, WebAuthn, SMS lub integracje z zewnętrznymi dostawcami.
  • Możliwość zarządzania politykami – kto musi korzystać z 2FA, wymuszanie ponownego logowania, różne poziomy „assurance”.
  • Obsługa grup i ról – mapowanie na role aplikacji webowej (RBAC/ABAC).
  • Biblioteki i dokumentacja dla Twojego stosu technologicznego (Java, .NET, Node.js, PHP, Python, SPA itp.).
  • Funkcje audytu – logi logowań, nieudanych prób, blokad, zmian konfiguracji.
  • Zgodność z regulacjami – RODO, możliwość hostingu w wybranym regionie, kontrola nad danymi.

Przykładowe opcje:

  • Keycloak – open source, bardzo elastyczny, dobra opcja dla środowisk on-premise lub gdy potrzebujesz pełnej kontroli nad IdP. Wymaga jednak więcej kompetencji administracyjnych.
  • Azure AD / Entra ID – naturalny wybór, jeśli organizacja korzysta z ekosystemu Microsoft 365. Szerokie wsparcie dla SSO, 2FA, warunkowego dostępu.
  • Auth0, Okta, Cognito – SaaS, szybki start, bogate możliwości konfiguracyjne, dobra dokumentacja. Koszt rośnie wraz ze skalą użytkowników.
  • Własny IdP – tylko jeśli masz silny, specjalistyczny zespół bezpieczeństwa i konkretne, nietypowe wymagania, których powyższe rozwiązania nie spełniają.

Jeżeli podczas analizy pojawia się pomysł „napiszmy własny IdP, bo będzie szybciej/taniej”, to bardzo mocny sygnał ostrzegawczy. Ogromna liczba luk bezpieczeństwa w implementacjach SSO wynika z domowych rozwiązań, które nie przeszły przeglądu eksperckiego ani testów penetracyjnych. Wybór IdP, który nie wspiera natywnie 2FA/MFA i nowoczesnych protokołów (OIDC) to przepis na kosztowną integrację i silne uzależnienie od własnych, trudnych w utrzymaniu rozszerzeń.

Białe cyfry 123 na jasnoniebieskim tle nawiązujące do logowania
Źródło: Pexels | Autor: Black ice

Projekt architektury logowania: przepływy, role i granice odpowiedzialności

Authorization code + PKCE jako standard „z wyższej półki”

W świecie OAuth2 istnieje kilka przepływów autoryzacji. Dla współczesnych aplikacji webowych (zwłaszcza SPA i mobilnych) aktualnym standardem jest authorization code flow z PKCE. Dla klasycznych aplikacji serwer-rendered authorization code bez PKCE jest nadal poprawny, ale dodanie PKCE zwiększa odporność na przechwycenie kodu autoryzacyjnego.

Kluczowe elementy authorization code + PKCE:

  • Aplikacja generuje code verifier (losowy, silny ciąg) oraz z niego code challenge (np. SHA-256 + base64url).
  • Do IdP w żądaniu /authorize wysyłany jest code challenge.
  • Po otrzymaniu kodu autoryzacyjnego aplikacja przesyła do /token code verifier, którego IdP używa do zweryfikowania, czy to ten sam klient, który inicjował logowanie.

Session management po stronie aplikacji: długość życia, odświeżanie, wygaszanie

Po decyzji o przepływie OAuth2/OIDC i jego poprawnej implementacji pojawia się kwestia, jak przełożyć tokeny na sesję aplikacyjną. To często pomijany etap, a właśnie tu powstaje wiele luk bezpieczeństwa.

Kluczowe pytania projektowe:

  • Czy aplikacja ma własną sesję (cookie), czy opiera się wyłącznie na access tokenie przesyłanym do API?
  • Jak długo trwa sesja aplikacyjna i czy jest zsynchronizowana z ważnością tokenów IdP?
  • Co się dzieje po wylogowaniu w IdP – czy sesja w aplikacji jest unieważniana, czy „żyje własnym życiem”?

W praktycznej aplikacji webowej bezpieczny model wygląda następująco:

  1. Aplikacja backendowa po otrzymaniu ważnego id_token/access_token tworzy lokalną sesję, reprezentowaną przez cookie httpOnly, secure, sameSite=lax/strict.
  2. W sesji przechowuje się tylko minimalne dane: identyfikator użytkownika, identyfikator sesji IdP, timestamp logowania, poziom uwierzytelnienia (np. MFA=on).
  3. Access tokenów nie trzyma się w przeglądarce w localStorage; najlepiej przechowywać je po stronie serwera, powiązane z sesją, albo w bezpiecznym, nietrwałym storage po stronie klienta (dla SPA – w pamięci).
  4. Czas życia sesji aplikacyjnej jest krótszy lub równy czasowi życia id_token, a przy użyciu refresh tokenów – powiązany z maksymalnym czasem sesji IdP.

Jeśli Twoja aplikacja utrzymuje sesję dłużej niż IdP, powstaje asymetria: użytkownik jest już wylogowany centralnie, ale wciąż zalogowany lokalnie. To bezpośrednio podkopuje sens SSO i 2FA. Punkt kontrolny: czas życia sesji aplikacji nie powinien przekraczać „session lifetime” skonfigurowanego w IdP, a najlepiej być o 10–20% krótszy.

Jeżeli audyt pokazuje, że aplikacja:

  • przechowuje access token w localStorage lub sessionStorage,
  • nie usuwa sesji przy centralnym wylogowaniu SSO,
  • nie ma mechanizmu odświeżania tokenów i obsługi błędu „token expired”,

to sygnał ostrzegawczy – integracja SSO z 2FA stanie się łatką na niespójny model sesji, a nie faktycznym wzmocnieniem bezpieczeństwa. Jeśli lokalna sesja nie jest spięta z logiką IdP, uzyskasz iluzję SSO, ale nie faktyczną kontrolę nad cyklem życia logowania.

Single Logout (SLO) i wylogowanie lokalne: pełne rozłączenie czy tylko „udawane”?

Wprowadzenie SSO i 2FA wymusza jasne rozróżnienie między wylogowaniem lokalnym a centralnym. Użytkownik klikający „Wyloguj” zwykle oczekuje, że „wyszedł ze wszystkiego”, a nie tylko z jednej aplikacji.

Typowe warianty wylogowania:

  • Wylogowanie lokalne – usunięcie sesji i cookie po stronie konkretnej aplikacji, bez komunikacji z IdP. Użytkownik nadal jest zalogowany w IdP i innych aplikacjach SSO.
  • Wylogowanie centralne (SLO) – aplikacja wywołuje endpoint logout IdP, a ten unieważnia sesję użytkownika oraz – w miarę możliwości – informuje pozostałe aplikacje o konieczności zakończenia ich sesji.

Strategia projektowa powinna odpowiedzieć na dwa pytania:

  1. Jakie zachowanie jest domyślne po kliknięciu „Wyloguj”? Czy zawsze wykonujesz SLO, czy dajesz użytkownikowi wybór („Wyloguj z tej aplikacji” vs „Wyloguj ze wszystkich aplikacji”)?
  2. Czy aplikacja umie obsłużyć „back-channel logout” (SAML, OIDC back-channel logout), jeśli IdP wykona wylogowanie centralne z innej aplikacji?

Jeżeli aplikacja nie implementuje żadnej formy obsługi SLO, użytkownik może zakończyć sesję w IdP, a mimo to pozostać zalogowany w Twoim systemie. To szczególnie niebezpieczne przy współdzielonych urządzeniach. Punkt kontrolny: wylogowanie centralne musi skutecznie unieważniać sesje lokalne, nawet jeśli użytkownik nie klika przycisku „Wyloguj” w każdej aplikacji po kolei.

Jeśli podczas przeglądu architektury pojawia się odpowiedź „nie wspieramy SLO, bo to skomplikowane”, ryzyko jest klarowne: SSO i 2FA będą funkcjonować tylko na wejściu, a nie w pełnym cyklu życia sesji. Przy systemach o podwyższonym rygorze (finanse, zdrowie) to obszar, który wymaga dopracowania przed dalszymi integracjami.

Granice odpowiedzialności między IdP, gateway i mikrousługami

Przy architekturach mikroserwisowych typową praktyką jest wprowadzenie API Gateway lub reverse proxy, który weryfikuje tokeny przed wpuszczeniem ruchu do usług. To dobry kierunek, pod warunkiem że granice odpowiedzialności są dobrze narysowane.

Przykładowy, przejrzysty podział:

  • IdP – uwierzytelnia użytkownika, realizuje 2FA/MFA, wystawia tokeny, przechowuje minimalny profil użytkownika.
  • API Gateway / Edge – waliduje tokeny (podpis, ważność, audience, issuer), odrzuca ruch bez ważnych tokenów, wycina wrażliwe nagłówki, ewentualnie wykonuje token exchange.
  • Mikrousługi – zakładają, że otrzymują tylko ruch z ważnymi tokenami; wykonują autoryzację na poziomie domeny biznesowej (uprawnienia, role, limity).

Błędy, które pojawiają się w audytach:

  • mikrousługi samodzielnie walidują tokeny, ale robią to niekonsekwentnie (różne klucze, różne biblioteki, raz sprawdzany audience, raz nie),
  • gateway tylko przekazuje token „jak leci”, bez jakiejkolwiek walidacji,
  • 2FA jest częściowo sprawdzane w aplikacji (np. „flaga MFA” zapisana w bazie), a częściowo w IdP – brak jednolitego źródła prawdy.

Punkt kontrolny: tylko IdP decyduje, czy użytkownik przeszedł 2FA, a aplikacja i mikrousługi jedynie konsumują ten fakt (np. poprzez acr, amr lub dedykowany claim w tokenie). Jeżeli w kilku miejscach systemu znajdują się własne flagi „czy przeszedł 2FA”, łatwo o rozjazdy i luki. Gdy gateway nie waliduje tokenów, każdy błąd w jednej z mikrousług staje się powierzchnią ataku.

Przygotowanie aplikacji pod integrację SSO – audyt aktualnego mechanizmu logowania

Inwentaryzacja aktualnego mechanizmu: gdzie i jak powstaje sesja

Zanim zacznie się konfigurować IdP, trzeba sprawdzić, jak Twoja aplikacja dziś zarządza logowaniem. Bez tego SSO z 2FA stanie się tylko dodatkową warstwą, a nie realną wymianą fundamentów.

Minimalna inwentaryzacja powinna odpowiedzieć na kilka pytań:

  • Gdzie jest zdefiniowana logika logowania? Jeden moduł, kilka kontrolerów, osobna usługa?
  • Jak wygląda cykl życia sesji? Kiedy jest tworzona, aktualizowana, wygaszana? Jak wygląda mechanizm „remember me”?
  • Jak są obsługiwane błędy logowania? Limity prób, blokady kont, captche, logi bezpieczeństwa?
  • Czy po stronie frontend są przechowywane jakiekolwiek wrażliwe dane (tokeny, hasła, identyfikatory sesji)?

Prosty sposób na wstępny audyt to przejście pełnego scenariusza logowania z włączonym narzędziem developerskim w przeglądarce:

  1. Otwórz zakładkę „Network”, zaloguj się klasycznym mechanizmem.
  2. Zidentyfikuj request, który kończy logowanie (HTTP 200/302 z redirectem).
  3. Sprawdź, jakie cookies są ustawiane, z jakimi flagami (httpOnly, secure, sameSite).
  4. Zweryfikuj, czy payload odpowiedzi zawiera jakiekolwiek tokeny lub dane, które nie powinny trafić do frontendu.

Jeżeli w trakcie tego ćwiczenia odnajdziesz:

  • cookie sesyjne bez flag secure i httpOnly,
  • identyfikator użytkownika i jego role w localStorage,
  • brak jawnej ścieżki wylogowania (tylko „czyszczenie” localStorage po stronie JS),

to wyraźny sygnał ostrzegawczy: trzeba najpierw uporządkować lokalny model sesji, a dopiero potem wprowadzać SSO. Jeśli baza jest dziurawa, SSO/2FA stają się wyłącznie kosmetyką.

Mapa przepływów logowania: od formularza do bazy danych

Drugim krokiem audytu jest narysowanie faktycznego przepływu logowania. Tu przydaje się podejście „kryterialne”: co dokładnie dzieje się z momentem kliknięcia „Zaloguj”?

Przykładowa sekwencja, którą trzeba zweryfikować:

  1. Użytkownik wysyła formularz z loginem i hasłem na endpoint /login.
  2. Backend pobiera użytkownika z bazy po loginie.
  3. Hasło jest porównywane z hashem (BCrypt, Argon2, PBKDF2).
  4. Tworzona jest sesja, ustawiane jest cookie, logowane jest zdarzenie.
  5. Wysyłany jest redirect na stronę główną.

Na każdym etapie szukaj punktów krytycznych:

  • Czy hasła są hashowane aktualnym, rekomendowanym algorytmem? Jeśli nie – konieczna będzie migracja przed integracją SSO.
  • Czy istnieje jakakolwiek 2FA po stronie aplikacji? Jeśli tak – jak jest wywoływana (przed sesją, po sesji, tylko dla wybranych użytkowników)?
  • Czy logowanie można wykonać poza oficjalnym formularzem (np. przez API publiczne, endpoint mobilny)?

Punkt kontrolny: docelowo tylko IdP ma prawo przyjmować hasło użytkownika. Jeżeli wiele mikroserwisów przyjmuje dane logowania albo istnieje kilka „alternatywnych” ścieżek logowania (np. stary endpoint SOAP wciąż aktywny), integracja SSO nie zamknie tych furt. Jeśli nie potrafisz jednym diagramem opisać wszystkich ścieżek logowania, trzeba je najpierw skonsolidować.

Identyfikacja miejsc zależnych od lokalnych kont i ról

Kolejny aspekt audytu to powiązania między tożsamością lokalną (użytkownicy w Twojej bazie) a rolami i uprawnieniami. Przy wprowadzaniu SSO często zmienia się źródło prawdy o użytkownikach – z lokalnej tabeli users na IdP.

Należy przeanalizować:

  • Jak są nadawane role i uprawnienia – ręcznie w panelu admina, automatycznie po rejestracji, importem z zewnętrznego systemu?
  • Czy identyfikator użytkownika jest wykorzystywany w wielu tabelach (FK), czy istnieje pośrednia warstwa (np. user_profile)?
  • Czy aplikacja oczekuje konkretnych pól (np. username, email, department) i czy te pola będą dostępne w tokenach IdP?

Typowy scenariusz integracji zakłada, że:

  1. IdP jest źródłem tożsamości (login, podstawowe atrybuty, 2FA).
  2. Aplikacja przechowuje lokalny profil, powiązany z identyfikatorem IdP (sub w OIDC, NameID w SAML).
  3. Role i uprawnienia aplikacji mogą być nadawane lokalnie lub mapowane z grup IdP (np. claim groups).

Jeżeli aplikacja oczekuje, że „username jest unikalny i niezmienny”, a IdP używa jako identyfikatora e-maila, który może się zmieniać – konflikt jest nieunikniony. Punkt kontrolny: ustal jeden, stabilny identyfikator użytkownika z IdP i oprzyj na nim mapowanie na lokalne konto. Jeżeli tego nie zrobisz, przy migracjach użytkownicy będą tracić dostęp do swoich danych.

Bezpieczeństwo haseł lokalnych przed migracją do SSO

Nawet jeśli docelowo chcesz całkowicie oddać hasła IdP, dziś Twoja baza prawdopodobnie zawiera ich kopie. To częsty element audytu bezpieczeństwa – integracja SSO bywa pretekstem do ich weryfikacji.

Lista kontrolna dla haseł lokalnych:

  • Algorytm – czy używany jest Argon2, Bcrypt z odpowiednim kosztem, PBKDF2 z dużą liczbą iteracji? MD5/SHA1/SHA256 bez soli to sygnał alarmowy.
  • Sól – czy jest unikalna per hasło, losowa, przechowywana razem z hashem? Brak soli lub współdzielona sól to krytyczny błąd.
  • Polityka haseł – minimalna długość, złożoność, rotacja, blokady po wielu nieudanych próbach.

Przed włączeniem logowania przez SSO rozsądnym krokiem jest:

  1. Zmodernizować algorytm (migracja „lazy” przy kolejnym logowaniu użytkownika).
  2. Oczyścić stare, nieużywane konta lub konta testowe.
  3. Strategia przejścia: tryb hybrydowy lokalne logowanie + SSO

    Rzadko kiedy możliwe jest wyłączenie lokalnego logowania z dnia na dzień. Najbezpieczniejszym etapem po audycie jest wprowadzenie trybu hybrydowego, w którym działają jednocześnie dwa mechanizmy: dotychczasowe logowanie i SSO z IdP.

    Podstawowe warianty hybrydy:

    • SSO jako opcja dodatkowa – klasyczny formularz logowania pozostaje, a obok pojawia się przycisk „Zaloguj przez <IdP>”.
    • SSO jako domyślna ścieżka – użytkownik jest najpierściej przekierowywany do IdP, ale formularz lokalny jest dostępny np. z osobnego linku dla administratorów lub kont serwisowych.
    • SSO tylko dla wybranych organizacji/tenantów – część użytkowników loguje się wyłącznie przez IdP (np. klienci korporacyjni), reszta nadal lokalnie.

    Przed wyborem wariantu warto odnieść się do kilku kryteriów:

    • czy istnieją konta techniczne, integracyjne lub serwisowe, które nie mogą być przeniesione do IdP,
    • czy użytkownicy są przyzwyczajeni do pojedynczego sposobu logowania (potencjalny opór przy nagłej zmianie),
    • czy umowy z klientami (SLA, wymogi compliance) dopuszczają logowanie tylko przez zewnętrznego dostawcę tożsamości.

    Punkt kontrolny: dokładnie zdefiniuj, kto i kiedy może jeszcze używać lokalnego logowania. Jeżeli „tymczasowe” formularze zostaną bez daty wyłączenia, z czasem zamienią się w stałą furtkę omijającą SSO i 2FA.

    Mapowanie kont lokalnych na tożsamości z IdP

    Tryb hybrydowy wymusza jasne zasady: jak powiązać istniejące konta z nowymi tożsamościami z IdP. Bez tego po pierwszym logowaniu SSO użytkownik może utworzyć sobie „drugie konto” w aplikacji, tracąc dostęp do danych z pierwotnego profilu.

    Najprostszy mechanizm mapowania obejmuje:

  1. Ustalenie stabilnego identyfikatora z IdP (np. sub, immutableId, employeeId).
  2. Utworzenie w lokalnej bazie pola na ten identyfikator (np. idp_subject w tabeli users).
  3. Jednorazowe powiązanie: przy pierwszym logowaniu SSO użytkownik jest „przypinany” do istniejącego konta lokalnego.

Powiązanie może być:

  • automatyczne – np. na podstawie unikalnego e-maila lub numeru pracownika,
  • półautomatyczne – aplikacja proponuje skojarzenie na podstawie e-maila, ale wymaga potwierdzenia przez użytkownika,
  • ręczne – administrator w panelu przypisuje tożsamość IdP do konkretnego konta, zwykle w systemach o podwyższonym ryzyku.

Sygnał ostrzegawczy: automatyczne łączenie tylko na podstawie displayName lub nieunikalnych pól (np. imię + nazwisko). W praktyce kończy się to błędnym przypisaniem uprawnień i trudnymi do cofnięcia konsekwencjami.

Punkt kontrolny: wymagaj co najmniej jednego atrybutu, który jest niezmienny lub rzadko zmieniany oraz unikalny w skali organizacji. Jeśli IdP nie udostępnia takiego pola, trzeba przemyśleć doposażenie IdP w dodatkowe atrybuty lub wprowadzić warstwę pośrednią (np. dedykowaną tabelę mapowań).

Porządkowanie ról i uprawnień przed włączeniem SSO

Integracja z IdP bywa okazją do skonfrontowania chaosu ról z rzeczywistością. Jeśli dziś użytkownik ma kilkadziesiąt ról, z których część dubluje się lub jest nieużywana, przeniesienie tego stanu do IdP tylko skomplikuje przepływy.

Minimum porządkowania obejmuje:

  • spisanie rzeczywiście używanych ról w aplikacji (na podstawie kodu i logów, nie tylko dokumentacji),
  • oznaczenie ról technicznych (np. INTERNAL_JOB_RUNNER) i wyłączenie ich z mapowania z IdP,
  • zidentyfikowanie ról, które są tak szerokie, że faktycznie oznaczają „admina wszystkiego” – to kandydaci do refaktoryzacji.

Kryterium przy integracji z IdP:

  • które role mają być nadawane wyłącznie lokalnie,
  • które mogą wynikać z grup IdP (np. członkostwo w grupie AD),
  • czy istnieje różnica między rolami funkcjonalnymi (np. „Analityk”) a organizacyjnymi (np. „Dział finansów”).

Przykład: w jednej z integracji grupy z Azure AD pełniły funkcję „poziom dostępu do projektu”, ale aplikacja miała osobne role „może eksportować dane” i „może usuwać raporty”. Po audycie ustalono, że grupy z IdP będą sterować zakresem projektów, a role techniczne zdefiniowano lokalnie. Dzięki temu zmiany w organizacji (przeniesienia pracowników) odbywały się w IdP, a logika biznesowa pozostała w aplikacji.

Punkt kontrolny: nie przenoś całej złożoności ról do IdP. IdP ma decydować o tym „kim jest użytkownik” w organizacji, a aplikacja – co taka osoba może robić w konkretnym systemie. Pomieszanie tych poziomów kończy się tym, że prosty change w aplikacji wymaga ingerencji w globalne grupy i polityki IdP.

Przygotowanie frontendu na nowe przepływy SSO i 2FA

Front nie powinien „znać” hasła użytkownika ani mechanizmów 2FA, ale musi umieć współpracować z nowym sposobem logowania. Podstawą jest rezygnacja z loginu/hasła po stronie SPA na rzecz redirectów do IdP.

Lista punktów kontrolnych dla frontendu:

  • Usunięcie obsługi hasła – brak przechowywania haseł w pamięci aplikacji, brak debug-logów z payloadem logowania, brak „pamiętaj hasło” realizowanego tokenem w localStorage.
  • Obsługa redirectów – poprawne wykrywanie stanu „niezalogowany” i przekierowanie do endpointu autoryzacji, który już kieruje do IdP.
  • Bezpieczne przechowywanie tokenów – jeżeli stosowany jest backend for frontend, frontend w ogóle nie dostaje tokenów; w wariancie public SPA stosowane są Authorization Code + PKCE i krótkie access tokeny trzymane w pamięci.

Element często pomijany: zachowanie frontendu przy odświeżaniu strony, otwieraniu wielu kart, powrocie po wygaśnięciu sesji. Jeżeli po stronie IdP sesja jest nadal ważna, ale sesja aplikacji wygasła, użytkownik powinien zostać „cicho” zalogowany ponownie (silent login) albo jasno poinformowany, co się stało. Brak spójności w tym obszarze generuje zgłoszenia typu „aplikacja co chwilę mnie wyrzuca” lub „losowo prosicie o 2FA”.

Punkt kontrolny: frontend nie implementuje własnego 2FA. Jakiekolwiek prośby o kod SMS, TOTP, U2F/FIDO2 powinny pochodzić wyłącznie z IdP, a aplikacja reaguje jedynie na wynik procesu (sukces/porażka) widoczny w parametrach powrotu lub w treści tokena.

Przeniesienie odpowiedzialności za 2FA z aplikacji do IdP

Jeżeli dziś 2FA jest zaimplementowane lokalnie (np. SMS z kodem, TOTP w Google Authenticator), konieczne jest wyraźne przesunięcie odpowiedzialności. Inaczej użytkownik będzie proszony o kod zarówno przez aplikację, jak i przez IdP, lub – gorzej – tylko lokalnie, z pominięciem IdP.

Typowa ścieżka migracji:

  1. Włączenie 2FA w IdP dla wybranej grupy testowej, bez wyłączania lokalnego 2FA.
  2. Dodanie w aplikacji logiki, która ufnie odczytuje poziom uwierzytelnienia z tokena (np. acr=urn:mfa:required).
  3. Stopniowe wyłączanie lokalnej 2FA: najpierw dla kont, które zalogowały się z poprawnym poziomem acr, potem globalnie.

W trakcie tego procesu należy sprawdzić:

  • czy IdP wymusza 2FA w scenariuszach wysokiego ryzyka (logowanie z nowego urządzenia, nietypowa lokalizacja),
  • czy po stronie IdP jest skonfigurowana polityka „step-up auth” – żądanie 2FA tylko w określonych sytuacjach (np. dostęp do szczególnie wrażliwych funkcji),
  • w jaki sposób IdP sygnalizuje poziom uwierzytelnienia w tokenie (acr, amr, niestandardowy claim).

Sygnał ostrzegawczy: aplikacja pyta o 2FA „dla świętego spokoju”, mimo że token potwierdza już uwierzytelnienie na poziomie MFA. Użytkownik odczuje to jako podwójne przeszkody, a zespół bezpieczeństwa – jako chaos w odpowiedzialnościach.

Punkt kontrolny: polityki wymuszania 2FA są skonfigurowane i egzekwowane wyłącznie w IdP. Aplikacja jedynie wymaga określonego poziomu acr dla krytycznych operacji; jeśli poziom jest niższy, wykonuje przekierowanie do IdP z żądaniem „step-up” zamiast pytać o drugi czynnik lokalnie.

Przegląd istniejących integracji i klientów API przed włączeniem SSO

Aplikacja funkcjonuje często w ekosystemie: mobilne aplikacje, integracje partnerskie, skrypty automatyzujące. Każda z tych integracji ma dzisiaj jakiś sposób autoryzacji (API keys, basic auth, tokeny sesyjne). Po wdrożeniu SSO te kanały nie mogą pozostać poza nową architekturą.

Minimum audytu integracji obejmuje:

  • spisanie wszystkich kluczy API, kont technicznych, kont „integracyjnych” powiązanych z zewnętrznymi systemami,
  • identyfikację mechanizmów logowania z pominięciem UI (np. logowanie w tle z loginem/hasłem),
  • sprawdzenie, czy którykolwiek z partnerów wykorzystuje udział konta użytkownika końcowego (delegowany dostęp) zamiast konta technicznego.

Docelowa architektura powinna rozróżniać:

  • użytkowników interaktywnych (ludzie, logujący się przez przeglądarkę lub aplikację mobilną) – tutaj SSO + 2FA jest standardem,
  • klientów maszynowych (joby, integracje, backend-to-backend) – tutaj stosuje się client credentials, klucze rotowane, certyfikaty, ale nie 2FA w klasycznym rozumieniu.

Sygnał ostrzegawczy: integracja partnera loguje się na konto zwykłego użytkownika z wyłączoną 2FA, bo „inaczej automat nie by działał”. To prosta droga do obejścia wszystkich polityk bezpieczeństwa wprowadzonej architektury SSO.

Punkt kontrolny: przed uruchomieniem SSO wszystkie zewnętrzne integracje mają jasno zdefiniowany tryb klienta maszynowego, z niezależnym mechanizmem uwierzytelniania (np. OAuth2 client credentials) i odseparowanymi uprawnieniami.

Plan migracji użytkowników: komunikacja, wyłączanie starego logowania, fallback

Gdy fundamenty są przygotowane (audyt, mapowanie kont, porządek w rolach, strategia dla integracji), ostatnim etapem przygotowania jest rozpisanie migracji jak projektu, a nie „jednorazowej akcji”. Tu kluczowe staje się nie tylko bezpieczeństwo, ale też ergonomia i przewidywalność zmian dla użytkowników.

Minimalne elementy planu migracji:

  • fazy wdrożenia – pilotaż na małej grupie, stopniowe rozszerzanie, ostateczne wyłączenie starego mechanizmu logowania,
  • komunikacja – jasne terminy, co się zmieni, jak wygląda nowa ścieżka logowania, gdzie zgłaszać problemy,
  • scenariusze awaryjne – co zrobić, gdy IdP ma incydent, a system jest krytyczny operacyjnie.

Praktyczny przykład: w jednym z systemów B2B zapowiedziano klientom, że przez miesiąc mechanizm lokalny i SSO będą działać równolegle. W tym czasie każdy użytkownik mógł samodzielnie „połączyć” swoje konto z tożsamością IdP klienta, po czym lokalny login był oznaczany jako „wygaszany” i po kolejnym miesiącu automatycznie blokowany. Raporty z tego procesu pozwalały zespołowi na bieżąco śledzić, jacy klienci zwlekają z migracją.

Punkt kontrolny: ustal konkretną datę i kryteria wyłączenia lokalnego logowania oraz zdefiniuj wyjątkowe ścieżki dostępu awaryjnego (np. konto break-glass, procedura zdalnego nadania dostępu). Jeżeli w planie istnieje tylko fraza „wyłączymy lokalne logowanie później”, to w praktyce nie zostanie wyłączone nigdy, a SSO z 2FA będzie jedynie „opcjonalnym dodatkiem”, nie nowym standardem.

Najczęściej zadawane pytania (FAQ)

Na czym polega integracja SSO z aplikacją webową krok po kroku?

Integracja SSO polega na tym, że aplikacja nie loguje użytkownika samodzielnie, tylko przekierowuje go do dostawcy tożsamości (IdP), a następnie przyjmuje i weryfikuje zwrotny komunikat (token lub asercję). Standardowy przepływ to: brak sesji w aplikacji → przekierowanie do IdP (/authorize) → logowanie w IdP → ewentualne 2FA → przekierowanie z kodem autoryzacyjnym → wymiana kodu na tokeny (/token) → utworzenie sesji w aplikacji.

Punkty kontrolne: poprawna konfiguracja redirect_uri, użycie właściwego przepływu (np. authorization code + PKCE), krytyczna weryfikacja tokenów (sygnatura, aud, iss, exp, nonce). Jeśli zespół nie potrafi narysować tego schematu krok po kroku, to sygnał ostrzegawczy, że integracja będzie podatna na błędy konfiguracji i luki bezpieczeństwa.

Jak wybrać między OIDC, SAML a własnym mechanizmem SSO?

Minimalnym sensownym wyborem dla nowych aplikacji webowych jest OAuth2 + OpenID Connect, bo ma dojrzałe biblioteki, jasne dobre praktyki (PKCE, nonce) i wspierają go główni dostawcy tożsamości. SAML bywa konieczny w starszych środowiskach korporacyjnych on‑premise, gdzie IdP klienta działa wyłącznie w tym standardzie. Własne SSO oparte na „domowych JWT” to zwykle ryzyko: brak standaryzacji, brak sprawdzonych bibliotek, większa szansa na krytyczne błędy walidacji tokenów.

Kryteria wyboru: profil klientów (B2C, B2B, korporacje), istniejące IdP po stronie organizacji, wymagania integracyjne (mobilne, SPA, legacy). Jeśli aplikacja ma wielu klientów korporacyjnych z różnymi IdP, często minimum to obsługa OIDC oraz mostkowanie SAML przez pośrednika (np. Keycloak jako broker).

Czym różni się SSO od logowania przez Google/Facebook?

Logowanie społecznościowe (Google, Facebook itp.) służy głównie do prostego uwierzytelniania użytkownika B2C, bez centralnego zarządzania politykami bezpieczeństwa w organizacji. Pełne SSO opiera się na zaufaniu między IdP a wieloma aplikacjami firmowymi i pozwala adminom egzekwować spójne polityki: hasła, 2FA, dostęp do grup aplikacji z jednego panelu.

Punkt kontrolny: kto naprawdę zarządza tożsamością i bezpieczeństwem użytkownika. Jeśli odpowiedź brzmi „Google użytkownika” zamiast „IdP organizacji”, to nie jest to klasyczne SSO, tylko wygodny skrót logowania bez formalnej kontroli korporacyjnej.

Czy po wdrożeniu SSO powinienem przestać przechowywać hasła w aplikacji?

W większości projektów najlepiej jest, aby po wdrożeniu SSO aplikacja całkowicie przestała zarządzać hasłami. Trzymanie równolegle lokalnych kont z hasłami i kont z IdP prowadzi często do dwóch źródeł prawdy, duplikatów kont i niespójnych polityk bezpieczeństwa. Wyjątek to świadomie zaprojektowany tryb hybrydowy, np. część użytkowników loguje się SSO, a część lokalnie (z jasno opisanymi zasadami).

To kluczowy punkt kontrolny na etapie projektu: podjąć decyzję, czy aplikacja po integracji SSO działa wyłącznie jako relying party IdP, czy nadal utrzymuje własny model haseł. Jeśli decyzja jest niejasna lub „zobaczymy w trakcie”, to sygnał ostrzegawczy, że architektura tożsamości w systemie szybko się rozmyje.

Jak poprawnie dołączyć 2FA/MFA do SSO w aplikacji webowej?

Bezpieczny i skalowalny wariant to realizacja 2FA po stronie IdP, a nie jako osobny, „domowy” moduł w aplikacji. Wtedy IdP – w zależności od polityki – po hasle wymusza drugi składnik (SMS, TOTP, WebAuthn/FIDO2), tworzy sesję SSO i dopiero potem przekierowuje użytkownika z powrotem. Aplikacja widzi tylko efekt: poprawnie uwierzytelnionego użytkownika, zrealizowane 2FA oraz odpowiednie atrybuty w tokenie.

Minimum: centralne definiowanie polityk (kto musi mieć 2FA, kiedy, jakimi metodami), brak lokalnego przechowywania sekretów TOTP i spójne logowanie zdarzeń bezpieczeństwa w IdP. Jeśli 2FA jest „opcjonalnym gadżetem” w jednej aplikacji i nie jest egzekwowane politykami IdP, to SSO głównie poprawia wygodę, a nie znacząco poziom bezpieczeństwa.

Jakie metody 2FA są najbezpieczniejsze przy SSO i których lepiej unikać?

W typowych aplikacjach webowych najczęściej stosuje się kody TOTP w aplikacjach mobilnych oraz SMS. TOTP zapewnia rozsądny balans między bezpieczeństwem a użytecznością i nie zależy od operatora GSM. Nowszy, wyższy standard to WebAuthn/FIDO2, który wykorzystuje klucze sprzętowe lub biometrię systemu operacyjnego i praktycznie eliminuje klasyczne phishingowe przechwycenia kodów.

SMS bywa wygodny na start i jako metoda awaryjna, ale jego słabym punktem są ataki typu SIM swap i przejęcie numeru. Dla systemów o wysokich wymaganiach SMS jako główna metoda 2FA to sygnał ostrzegawczy. Minimalne podejście: SMS tylko jako fallback, a jako podstawę stosować TOTP lub WebAuthn, egzekwowane polityką IdP.

Jak sprawdzić, czy integracja SSO + 2FA jest poprawnie zaprojektowana?

Dobry test to przejście pełnej ścieżki użytkownika na tablicy: brak sesji → przekierowanie do IdP → logowanie → 2FA → powrót z kodem → wymiana na tokeny → utworzenie sesji w aplikacji → dostęp do kolejnej aplikacji w tym samym IdP bez ponownego logowania. Jeśli zespół nie potrafi opisać, gdzie dokładnie powstaje sesja SSO, kiedy wymuszane jest ponowne 2FA i jakie atrybuty są w tokenie, projekt wymaga doprecyzowania.

Lista kryteriów minimum: jedno źródło prawdy o tożsamości (IdP), 2FA egzekwowane politykami IdP, brak lokalnych „dodatkowych” haseł, jawnie opisane redirect_uri i przepływy OAuth2/OIDC, weryfikacja tokenów po stronie aplikacji oraz spójne logowanie zdarzeń bezpieczeństwa. Jeśli choć dwa z tych elementów są „na słowo honoru”, integracja jest kandydatem do audytu przed wdrożeniem produkcyjnym.

Najważniejsze punkty

  • SSO przenosi pełną odpowiedzialność za uwierzytelnianie do IdP, więc aplikacja nie powinna już samodzielnie przechowywać ani weryfikować haseł – utrzymywanie równolegle lokalnych kont i SSO to sygnał ostrzegawczy dotyczący spójności tożsamości.
  • Dwuskładnikowe uwierzytelnianie ma realny sens dopiero wtedy, gdy jest centralnie egzekwowane po stronie IdP, z jasno zdefiniowaną polityką (kto, kiedy i jakiego 2FA używa), a nie jako „domowa” nakładka w pojedynczej aplikacji.
  • Wybór metody 2FA powinien uwzględniać poziom ryzyka: TOTP jest standardowym minimum, SMS jest wygodny, ale słaby bezpieczeństwa, natomiast WebAuthn/FIDO2 to docelowy kierunek dla systemów o podwyższonych wymaganiach.
  • Poprawnie zintegrowane SSO + 2FA oznacza, że użytkownik loguje się i przechodzi drugi składnik raz w IdP, a później przełącza się między aplikacjami bez ponownych pytań o hasło i kody – chyba że polityka wymusi ponowne uwierzytelnienie dla operacji krytycznych.
  • Kluczowym punktem kontrolnym przed konfiguracją jest rozrysowanie pełnej ścieżki logowania (przekierowanie do /authorize, sesja SSO po stronie IdP, wymuszenie 2FA, wymiana kodu na tokeny, weryfikacja tokenów, sesja w aplikacji); jeśli zespół nie potrafi jej jasno opisać, integracja jest wysokiego ryzyka.