Biblioteki do obsługi OAuth2 i OpenID Connect w aplikacjach webowych krok po kroku

0
22
3/5 - (1 vote)

Nawigacja:

Od czego zacząć: cele, scenariusze i słowniczek pojęć

Jakie problemy realnie rozwiązuje OAuth2 i OpenID Connect

W typowej aplikacji webowej szybko pojawia się kilka powtarzalnych problemów: bezpieczne logowanie użytkowników, delegowanie uprawnień do API, integracja z zewnętrznymi dostawcami tożsamości (Google, Azure AD, Keycloak), obsługa sesji pomiędzy wieloma usługami oraz bezpieczne przechowywanie danych logowania. OAuth2 i OpenID Connect (OIDC) porządkują te obszary, zamiast tworzenia prowizorycznych rozwiązań ad hoc.

OAuth2 odpowiada przede wszystkim na pytanie: czy dana aplikacja może działać w imieniu użytkownika lub w swoim własnym imieniu?. Dostarcza standardowego sposobu pozyskiwania tokenów dostępu do API, określania zakresów (scopes) oraz wygaszania i odświeżania dostępu. OpenID Connect rozszerza ten mechanizm, dodając warstwę uwierzytelniania – czyli: kim jest użytkownik, jakie ma identyfikatory, atrybuty i w jaki sposób można je bezpiecznie przekazać aplikacji.

Z punktu widzenia aplikacji webowej biblioteki OAuth2 i OpenID Connect służą do dwóch rzeczy:

  • obsługi pełnego procesu logowania/wylogowania z przekierowaniami, sesją i tokenami,
  • bezpiecznego dołączania tokenów do zapytań HTTP w kierunku API oraz ich weryfikacji po stronie serwera.

Dzięki standardowi i dojrzałym bibliotekom integracja z dowolnym dostawcą tożsamości przestaje być jednorazowym „projektem specjalnym”, a staje się powtarzalnym, przewidywalnym procesem z jasno zdefiniowanymi krokami.

Autoryzacja kontra uwierzytelnianie – kluczowa różnica

OAuth2 to protokół autoryzacji. Odpowiada na pytanie: czy ta aplikacja (klient) może zrobić X w imieniu Y?. Nie mówi natomiast nic konkretnego o tym, kim jest użytkownik Y. Ta różnica jest krytyczna, bo wiele starszych tutoriali „przy okazji” używało OAuth2 do pseudo‑logowania, co prowadziło do niejednoznacznych, trudnych do utrzymania integracji.

OpenID Connect jest warstwą nad OAuth2, która standaryzuje uwierzytelnianie użytkownika. Dzięki OIDC aplikacja dostaje specjalny token – ID token – zawierający ustandaryzowane informacje o użytkowniku w postaci JSON Web Token (JWT). Można więc bezpiecznie i jednoznacznie stwierdzić, kto się zalogował, kiedy token wygaśnie oraz z jakiego źródła pochodzi.

Biblioteka do obsługi OIDC zwykle rozwiązuje dwa zadania naraz: wykonuje przepływy OAuth2 (żeby uzyskać tokeny do API) oraz poprawnie obrabia ID token (czyli właściwe logowanie użytkownika). W praktyce oznacza to mniej „klejenia” własnymi rękami i mniejszą liczbę miejsc, w których można popełnić błąd bezpieczeństwa.

Typowe scenariusze: server-side, SPA + API, mobilka + backend

Wybór biblioteki OAuth2 i OIDC oraz właściwego przepływu zależy mocno od architektury. Najczęściej spotykane scenariusze to:

  • Aplikacja server-side (MVC) – np. Spring MVC, ASP.NET MVC, Django. Logowanie realizowane jest za pomocą przekierowań, a sesja użytkownika trzymana w ciasteczku (cookie) pomiędzy żądaniami. Tu sprawdzają się wysokopoziomowe integracje frameworkowe, które same obsługują przekierowania, tokeny i sesję.
  • SPA + API – frontend w React/Angular/Vue, API w Node/Java/.NET. Logowanie użytkownika najczęściej odbywa się w przeglądarce z użyciem Authorization Code + PKCE, a tokeny są używane do autoryzacji wywołań API. W takim scenariuszu potrzebne są osobne biblioteki po stronie frontendu i backendu.
  • Mobilka + backend – aplikacja mobilna (Android/iOS) komunikuje się z IdP i backendem. Mechanizmy są podobne jak w SPA, ale detale (np. obsługa redirect URI) nieco inne. Tutaj często korzysta się z bibliotek mobilnych dostarczanych przez IdP lub OSS (np. AppAuth).

Większość bibliotek OAuth2/OIDC adresuje konkretny scenariusz. Próba używania biblioteki „od SPA” w klasycznym MVC albo odwrotnie zwykle kończy się walką z frameworkiem i dziwnymi hackami. Dobór narzędzia do architektury to pierwszy i bardzo ważny krok.

Minimalny słowniczek pojęć, bez których trudno ruszyć dalej

Kilka terminów pojawia się w każdej konfiguracji OAuth2 i OpenID Connect. Dobrze mieć je „pod ręką”, żeby konfiguracja bibliotek nie była serią zgadywanek:

  • Resource owner – najczęściej użytkownik końcowy, którego dane lub zasoby są chronione.
  • Client – aplikacja, która chce uzyskać dostęp do zasobów (np. Twoja aplikacja webowa, SPA, backend).
  • Authorization server / Identity Provider (IdP) – serwer autoryzacji i tożsamości: Keycloak, Azure AD, Auth0, Okta, serwer wbudowany w Keycloak lub inny system IAM.
  • Resource server – API lub inny serwer zasobów, który przyjmuje i weryfikuje access token.
  • Scope – zakresy uprawnień proszonych przez klienta (np. openid, profile, email, api.read).
  • Claim – pojedyncza informacja o użytkowniku lub tokenie (np. sub, email, role, exp).
  • Access token – token używany do wywoływania API. Ma ograniczony czas życia i zakres.
  • ID token – token zawierający informacje o zalogowanym użytkowniku, używany głównie przez aplikację kliencką do stworzenia sesji.
  • Refresh token – token używany do odświeżania access tokenu bez ponownego logowania użytkownika.

Single sign-on, logowanie przez zewnętrzne IdP i własny dostawca tożsamości

W praktyce celem wdrożenia bibliotek OAuth2 i OIDC jest często:

  • wdrożenie single sign-on (SSO) pomiędzy wieloma aplikacjami,
  • logowanie zewnętrzne – Google, Azure AD, Keycloak, GitHub, Facebook,
  • spięcie aplikacji z własnym serwerem tożsamości (On-Prem, Keycloak, Duende IdentityServer, WSO2 itp.).

W każdym z tych przypadków logika w aplikacji klienckiej jest niemal identyczna – zmienia się głównie konfiguracja IdP. Dlatego tak ważna jest umiejętność korzystania z bibliotek OAuth2 i OIDC w sposób konfigurowalny, a nie przywiązany na stałe do jednego konkretnego dostawcy.

Palec wpisujący kod dostępu na ekranie blokady smartfona
Źródło: Pexels | Autor: indra projects

Przegląd bibliotek i stosów technologicznych – jak wybrać narzędzie

Biblioteki wysokopoziomowe vs niskopoziomowe

Biblioteki do obsługi OAuth2 i OpenID Connect można podzielić na dwie duże grupy:

  • Wysokopoziomowe (frameworkowe) – silnie zintegrowane z konkretnym frameworkiem, np. Spring Security, ASP.NET Core Authentication, Django z dedykowanym middleware. Zwykle wystarczy konfiguracja w pliku/yamlu/kodzie, a biblioteka przejmuje przekierowania, sesje, weryfikację tokenów i błędów.
  • Niskopoziomowe – biblioteki, które oferują klasy do wykonywania żądań OAuth2/OIDC (np. po HTTP), parsowania i weryfikacji JWT, ale nie wiedzą nic o Twoim frameworku. Przykład: Authlib w Pythonie, The PHP League OAuth2 Client czy surowe SDK OAuth2 w JS.

Wysokopoziomowa biblioteka oszczędza ogromną ilość czasu przy typowych scenariuszach (np. logowanie do aplikacji MVC, zabezpieczenie API). Niskopoziomowa biblioteka przydaje się, gdy:

  • masz nietypowy przepływ,
  • korzystasz z mniej popularnego frameworka lub architektury,
  • musisz dopasować się do bardzo specyficznych wymagań bezpieczeństwa albo integrujesz wiele IdP równocześnie.

Biblioteki OAuth2/OIDC w popularnych ekosystemach

Kilka stosów technologicznych, które są najczęściej spotykane w aplikacjach webowych i ich typowe biblioteki:

  • Java:
    • Spring Security – gotowe wsparcie dla OAuth2 Login, Client, Resource Server, integracja z OIDC.
    • Quarkus OIDC – rozszerzenie do Quarkusa do integracji z OIDC (Keycloak, inne IdP).
  • .NET:
    • Wbudowane OpenIdConnect i OAuth2 w ASP.NET Core (middleware AddOpenIdConnect, AddJwtBearer).
    • Duende IdentityServer – serwer OIDC/OAuth2 (IdP) dla .NET, ale zawiera też klienta i przykłady integracji.
  • JavaScript / TypeScript:
    • oidc-client-ts – klient OIDC dla SPA (następca oidc-client-js).
    • NextAuth.js – wyspecjalizowany w logowaniu i sesjach w Next.js, obsługuje wielu providerów.
    • angular-oauth2-oidc – popularna biblioteka dla Angulara.
  • Python:
    • Authlib – uniwersalna biblioteka OAuth/OIDC, integracje z Flask, Django, Starlette i innymi.
    • Django OAuth Toolkit – raczej jako serwer OAuth2, ale bywa używany też po stronie klienta.
  • PHP:
    • Laravel Socialite – logowanie przez zewnętrznych providerów (OAuth2/OIDC) w aplikacjach Laravel.
    • The PHP League OAuth2 Client – uniwersalny klient OAuth2 w PHP, możliwa integracja z OIDC.

W praktyce biblioteki frameworkowe mają lepszą dokumentację pod konkretny przypadek (np. „logowanie przez Google w aplikacji Laravel”), natomiast biblioteki ogólne (Authlib, The PHP League) dają większą elastyczność i lepiej się nadają do bardziej złożonych architektur.

Kryteria wyboru biblioteki OAuth2/OIDC

Przy wyborze biblioteki do obsługi OAuth2 i OpenID Connect warto przejść przez krótką checklistę:

  • Dojrzałość i popularność – liczba gwiazdek, aktywność repozytorium, liczba pobrań czy referencji w artykułach.
  • Dokumentacja – czy są przykłady dla Twojego scenariusza (MVC, SPA, API), czy dokumentacja jest aktualna względem wersji.
  • Zgodność ze standardem – wsparcie dla OIDC, Authorization Code + PKCE, obsługa discovery (/.well-known/openid-configuration), weryfikacja podpisów JWT.
  • Wsparcie społeczności – aktywne Issues, dyskusje, blogi, StackOverflow.
  • Licencja – zwłaszcza w projektach komercyjnych, trzeba zweryfikować warunki użycia (np. Duende zmieniło licencjonowanie względem dawnego IdentityServer4).

Jeżeli różne biblioteki wydają się podobne, opłaca się przejrzeć gotowe sample od IdP, z którego chcesz korzystać (np. Keycloak, Auth0, Azure AD często udostępniają przykłady integracji z konkretnymi bibliotekami). To znacząco skraca czas pierwszego wdrożenia.

Różnica między integracją frameworkową a samodzielnym klientem OIDC

Biblioteka frameworkowa, taka jak Spring Security czy ASP.NET Core Authentication, nie tylko obsługuje same tokeny, ale też:

  • integruje się z systemem routingów i middleware,
  • udostępnia abstrakcję na użytkownika (np. Principal, ClaimsPrincipal),
  • umożliwia podłączanie autoryzacji na poziomie atrybutów/annotacji (@PreAuthorize, [Authorize]),
  • ma gotowe mechanizmy sesji, cookies, CSRF, logowania/wylogowania.

Samodzielny klient OIDC (np. Authlib, The PHP League OAuth2 Client) dostarcza narzędzia do zbudowania tego wszystkiego, ale nie narzuca konkretnego sposobu integracji z Twoim frameworkiem. Zyskujesz elastyczność, tracisz część „magii”. Taki wybór ma sens przy mikroframeworkach, własnych rozwiązaniach lub gdy integrujesz kilka IdP naraz w bardzo specyficzny sposób.

Kiedy rozważyć SaaS zamiast własnego IdP

Zamiast stawiać własny serwer autoryzacji (np. Keycloak, Duende IdentityServer), część zespołów wybiera usługę SaaS: Auth0, Okta, Amazon Cognito, Azure AD B2C i podobne. To często dobry kierunek, gdy:

  • nie ma w zespole kompetencji IAM/bezpieczeństwa,
  • potrzebne jest szybkie wdrożenie logowania z wieloma providerami,
  • liczy się zgodność z certyfikacjami i audytami bezpieczeństwa dostawcy.

Biblioteki klienckie po stronie aplikacji webowej działają wtedy praktycznie tak samo – zmienia się tylko konfiguracja endpointów i dane klienta. Różnica dotyczy głównie zarządzania samym IdP, nie mechanizmu integracji po stronie aplikacji.

Przepływy OAuth2 i OIDC, które mają znaczenie w aplikacjach webowych

Authorization Code z PKCE – domyślny wybór dla aplikacji webowych

Najbardziej uniwersalnym i rekomendowanym przepływem jest Authorization Code z rozszerzeniem PKCE (Proof Key for Code Exchange). Sprawdza się zarówno w klasycznych aplikacjach server-side (MVC), jak i w SPA, które rozmawiają z backendem przez API.

Przepływ polega na tym, że aplikacja:

  1. przekierowuje użytkownika do IdP z parametrami (m.in. client_id, redirect_uri, scope, response_type=code),
  2. IdP loguje użytkownika i odsyła z powrotem na redirect_uri z kodem autoryzacji,
  3. aplikacja backend (lub biblioteka) wymienia kod na access token (i często ID token, refresh token) na dedykowanym endpointcie IdP.

PKCE dodaje do tego mechanizm code_verifier i code_challenge, który chroni przed przechwyceniem kodu autoryzacyjnego. Po stronie klienta generowany jest losowy ciąg (code_verifier), jego skrót wysyłany jest jako code_challenge przy pierwszym przekierowaniu, a przy wymianie kodu na tokeny klient udowadnia, że jest w posiadaniu oryginalnego code_verifier.

Dla aplikacji webowych, szczególnie SPA, jest to obecnie standard de facto – wiele IdP domyślnie wymaga PKCE dla publicznych klientów.

Implicit Flow – dlaczego już nie jest zalecany

Starsze aplikacje SPA często wykorzystują Implicit Flow, w którym access token (a czasem ID token) przekazywany jest bezpośrednio w adresie URL po przekierowaniu z IdP. Kiedyś miało to sens ze względu na ograniczenia przeglądarek, ale dziś ten przepływ uznawany jest za przestarzały i mniej bezpieczny.

Problemy z Implicit Flow to między innymi:

  • token pojawia się w URL, bywa logowany w historii, logach serwera, narzędziach analitycznych,
  • brak etapu wymiany kodu na token po stronie serwera utrudnia dodatkowe zabezpieczenia (jak PKCE),
  • część IdP ogranicza lub całkowicie wyłącza wsparcie dla tego przepływu.

Jeżeli istniejąca aplikacja korzysta z Implicit Flow, dobrym kierunkiem jest stopniowa migracja do Authorization Code + PKCE, zwykle przy wsparciu tej samej biblioteki frontowej (większość nowoczesnych bibliotek OIDC to umożliwia).

Client Credentials – dla połączeń serwer-serwer

Przepływ Client Credentials nie służy do logowania użytkowników, tylko do uwierzytelniania samej aplikacji (klienta) przed API. Pasuje do scenariuszy typu:

  • mikroserwisy komunikujące się wzajemnie,
  • zadania w tle (cron, worker),
  • integracje systemowe (np. system ERP wywołujący API sklepu).

W tym przepływie nie ma przeglądarki ani interakcji użytkownika. Klient przesyła do IdP swój client_id i client_secret (czasem dodatkowo podpisany JWT) i otrzymuje access token, którym wywołuje API. W bibliotekach frameworkowych często sprowadza się to do prostego klienta HTTP skonfigurowanego z możliwością automatycznego odświeżania tokenu.

Device Code i inne specjalne przepływy

Istnieją też mniej typowe przepływy, z którymi można się spotkać przy bardziej złożonych architekturach:

  • Device Code – używany na urządzeniach bez wygodnej przeglądarki (TV, terminale). Użytkownik wprowadza kod na innym urządzeniu, a aplikacja czeka na potwierdzenie.
  • Resource Owner Password Credentials (ROPC) – aplikacja zbiera login i hasło użytkownika bezpośrednio i wysyła do IdP. Przepływ uznawany za niebezpieczny i silnie odradzany, ale wciąż bywa używany w projektach legacy.

Standardowe login przez przeglądarkę w aplikacjach webowych praktycznie zawsze można zrealizować bez ROPC – biblioteki OAuth2/OIDC są projektowane właśnie pod takie scenariusze.

Który przepływ wybrać w jakim scenariuszu

Najczęstsze kombinacje to:

  • Aplikacja MVC + API (ten sam backend) – Authorization Code (często bez PKCE, bo backend jest „confidential client”, ale coraz częściej z PKCE dla spójności).
  • SPA + API – Authorization Code + PKCE, access token przechowywany zwykle tylko po stronie backendu (BFF) lub krótkotrwale w pamięci JS.
  • Mikroserwisy – Client Credentials pomiędzy serwisami, a dla użytkownika końcowego Authorization Code + OIDC.

Jeśli konfiguracja kilku przepływów naraz brzmi groźnie, można zacząć od jednego – Authorization Code + OIDC – i dopiero potem dodać Client Credentials dla komunikacji backend-backend.

Zbliżenie ekranu smartfona z komunikatem weryfikacji konta
Źródło: Pexels | Autor: Zulfugar Karimov

Przygotowanie środowiska i konfiguracja dostawcy tożsamości (IdP)

Wybór i uruchomienie IdP – lokalnie i w środowiskach testowych

Na etapie nauki lub w projekcie proof-of-concept najwygodniej jest uruchomić IdP lokalnie, np. w Dockerze. Typowe opcje:

  • Keycloak – popularny, open source, bogata konfiguracja, gotowe obrazy Docker.
  • Auth0 / Okta / Azure AD B2C – konta testowe w chmurze, nie trzeba nic instalować, ale wymaga rejestracji.
  • Duende IdentityServer – dla świata .NET, często uruchamiany razem z aplikacją lub w osobnej usłudze.

Dobrą praktyką jest posiadanie odrębnych środowisk IdP (dev, test, prod), nawet jeśli początkowo wszystkie działają na jednym serwerze. Ułatwia to zmianę konfiguracji bez ryzyka przerwania działania produkcji.

Rejestracja klienta (aplikacji) w IdP

Każda aplikacja korzystająca z OAuth2/OIDC musi być zarejestrowana jako klient w IdP. W panelu administracyjnym zwykle trzeba podać kilka kluczowych parametrów:

  • Client ID – publiczny identyfikator aplikacji.
  • Client Secret – „hasło” dla aplikacji backendowej (confidential client); nie używa się go w SPA.
  • Redirect URI – adres, pod który IdP odeśle użytkownika po zalogowaniu (np. https://localhost:5001/signin-oidc).
  • Post-logout redirect URI – adres, pod który użytkownik trafi po wylogowaniu z IdP.
  • Allowed scopes – zakresy uprawnień (np. openid, profile, email, niestandardowe API).

Kłopoty przy pierwszej konfiguracji niemal zawsze wynikają z literówek w redirect URI lub braku odpowiednich scope. Jeżeli aplikacja „mówi”, że nie może wymienić kodu na token, pierwsza rzecz do sprawdzenia to zgodność redirect_uri po stronie IdP i klienta.

Konfiguracja realm/tenant i użytkowników testowych

IdP zwykle grupuje aplikacje i użytkowników w realmy, tenanty lub projekty. W Keycloak będzie to realm, w Azure AD – tenant, w Auth0 – tenant/domain.

Dla wygody:

  • utwórz osobny realm/tenant na potrzeby testów,
  • załóż kilku użytkowników testowych z różnymi rolami (np. user, admin),
  • przypisz im grupy/role i skonfiguruj mapowanie tych ról do claimów w tokenie (np. roles w ID token).

Dzięki temu można od razu ćwiczyć autoryzację, a nie tylko sam mechanizm logowania.

Discovery dokument i endpointy protokołu

Większość IdP wystawia tzw. document discovery pod adresem:

https://<domena-idp>/.well-known/openid-configuration

Ten dokument zawiera adresy wszystkich istotnych endpointów:

  • authorization_endpoint – przekierowanie użytkownika do logowania,
  • token_endpoint – wymiana kodu na token,
  • userinfo_endpoint – opcjonalny endpoint z dodatkowymi danymi o użytkowniku,
  • jwks_uri – klucze publiczne do weryfikacji podpisu JWT.

Biblioteki wysokopoziomowe często potrafią same pobrać ten dokument na podstawie authority/issuer (np. https://idp.example.com/realms/demo) i skonfigurować resztę parametrów automatycznie. W aplikacjach własnych/niskopoziomowych dobrze jest najpierw obejrzeć discovery dokument w przeglądarce lub curlu, żeby upewnić się, jakie endpointy są faktycznie dostępne.

Scopes, claims i profile standardowe

W OIDC znaczenie ma nie tylko sam access token, lecz także zawartość ID tokenu. To, jakie dane o użytkowniku się tam znajdą, wynika z:

  • zakresów (scopes) żądanych przez klienta,
  • mapowania claimów po stronie IdP.

Podstawowe scope w OIDC:

  • openid – wymagany, aktywuje OIDC i ID token,
  • profile – podstawowe dane profilu (imię, nazwisko, pseudonim),
  • email – adres e‑mail i informacja o jego potwierdzeniu,
  • offline_access – często używany do wydania refresh tokenu.

Jeżeli aplikacja potrzebuje własnych claimów (np. tenant_id, permissions), można w IdP skonfigurować custom claims i powiązać je z rolami, grupami lub atrybutami użytkownika. Dzięki temu biblioteka po stronie aplikacji odbiera już komplet informacji, a logika w kodzie sprowadza się do odczytania odpowiednich claimów z tokenu.

Integracja w aplikacji server-side krok po kroku

Architektura: sesja cookie vs stateless JWT

Aplikacja server-side (np. Java / .NET / Python) może zarządzać uwierzytelnieniem na dwa główne sposoby:

  • Sesja + cookie – backend trzyma informacje o zalogowanym użytkowniku w sesji (w pamięci, cache, bazie), a przeglądarka otrzymuje session cookie. Tokeny z IdP są przechowywane po stronie serwera.
  • Stateless JWT – backend przechowuje minimalny stan, a cała informacja o użytkowniku jest zakodowana w JWT (ID token lub access token) przesyłanym z przeglądarki przy każdym żądaniu.

Większość frameworków wysokopoziomowych przy klasycznym modelu MVC stawia na sesje + cookie, co ma kilka zalet: tokeny nie lądują w przeglądarce, łatwiej obsłużyć wylogowanie i wygasanie sesji. JWT w frontendzie przydaje się bardziej przy architekturze „API + SPA”, do której można dojść później.

Konfiguracja klienta OIDC po stronie backendu

Niezależnie od języka, konfiguracja zwykle sprowadza się do tych samych pól. Przykładowe parametry (w formie ogólnej):

authority:        https://idp.example.com/realms/demo
client_id:        my-web-app
client_secret:    <sekret> (dla aplikacji backendowych)
redirect_uri:     https://localhost:5001/signin-oidc
post_logout_uri:  https://localhost:5001/signout-callback-oidc
response_type:    code
scope:            openid profile email api.read

W Spring Security trafią one do konfiguracji spring.security.oauth2.client, w ASP.NET Core – do AddOpenIdConnect w Program.cs lub appsettings.json, a w Pythonie/Flasku do obiektu konfiguracyjnego używanego przez Authlib.

Po poprawnym ustawieniu tych wartości większość „magii” dzieje się sama: biblioteka generuje adres logowania, obsługuje callback, wymienia kod na tokeny i tworzy kontekst użytkownika (Principal, ClaimsPrincipal, obiekt request.user itp.).

Obsługa logowania: przekierowanie do IdP i callback

Aby uruchomić logowanie, backend musi:

  1. udostępnić endpoint typu /login (czasem generowany automatycznie),
  2. zainicjować przepływ OIDC – czyli przekierować użytkownika pod authorization_endpoint IdP z odpowiednimi parametrami,
  3. obsłużyć callback pod zdefiniowanym redirect_uri.

Frameworki zwykle dodają domyślne ścieżki (np. /signin-oidc w ASP.NET Core, /login/oauth2/code/{registrationId} w Spring Security), ale w razie potrzeby można je zmienić. Kluczowe jest, by te ścieżki były identyczne z tym, co wpisane jest w konfiguracji klienta w IdP.

Na etapie callbacku biblioteka:

  • sprawdza parametr state, by uniknąć ataków CSRF,
  • Najczęściej zadawane pytania (FAQ)

    Jaka jest różnica między OAuth2 a OpenID Connect w aplikacji webowej?

    OAuth2 zajmuje się autoryzacją, czyli odpowiedzią na pytanie: czy ta aplikacja może wykonać daną operację w imieniu użytkownika lub swoim własnym. Dzięki niemu aplikacja dostaje access token i może wywoływać chronione API w określonym zakresie (scope).

    OpenID Connect (OIDC) rozszerza OAuth2 o warstwę uwierzytelniania. Dostarcza ID token w formacie JWT z informacjami o użytkowniku (claimy typu sub, email, name). Dzięki temu aplikacja wie, kto się zalogował i może bezpiecznie zbudować sesję użytkownika, zamiast „dorabiać” logowanie na samym OAuth2.

    Jaką bibliotekę OAuth2 / OpenID Connect wybrać do mojej aplikacji webowej?

    Pierwszy krok to ustalenie architektury: klasyczne server-side MVC, SPA + API czy mobilka + backend. Dla aplikacji MVC zwykle najlepiej sprawdzają się wysokopoziomowe integracje frameworkowe (np. Spring Security, ASP.NET Core Authentication, middleware w Django), które biorą na siebie przekierowania, sesję i weryfikację tokenów.

    W modelu SPA + API przyjmuje się osobne biblioteki dla frontendu (np. klient OIDC dla React/Angular/Vue) oraz backendu (middleware JWT/OAuth2 po stronie API). Jeśli korzystasz z mniej popularnego frameworka lub masz niestandardowe przepływy, lepsze będą biblioteki niskopoziomowe, które dają większą elastyczność kosztem większej ilości kodu konfiguracyjnego.

    Od czego zacząć wdrażanie logowania przez OAuth2 / OIDC krok po kroku?

    Na starcie jasno określ scenariusz: czy chodzi tylko o logowanie użytkownika, czy też o dostęp do wielu API, SSO między aplikacjami, integrację z zewnętrznym IdP (np. Google, Azure AD, Keycloak). To pozwala dobrać odpowiedni przepływ (najczęściej Authorization Code + PKCE) i zdecydować, czy wystarczy biblioteka klienta, czy potrzebne będzie też własne IdP.

    Następnie: wybierz bibliotekę dopasowaną do frameworka, skonfiguruj podstawowe parametry (issuer/authority, client_id, redirect_uri, scopes) i przetestuj pełen cykl logowania/wylogowania. Dostęp do API i obsługę access tokenów dodawaj dopiero, gdy logowanie i sesja użytkownika działają stabilnie.

    Jak bezpiecznie przechowywać i wysyłać tokeny w aplikacji webowej?

    W aplikacjach server-side sesja użytkownika zwykle trzymana jest w cookie, a access token może być przechowywany po stronie serwera. Frameworkowe biblioteki same dbają o skojarzenie sesji z tokenami i ich odświeżanie, więc nie trzeba ręcznie wstrzykiwać tokenów w przeglądarce.

    W SPA sytuacja jest wrażliwsza. Tokenów lepiej nie trzymać w localStorage ani sessionStorage (bo łatwiej je przechwycić przy XSS). Bezpieczniejsze są rozwiązania oparte na secure HttpOnly cookies, krótkich access tokenach oraz refresh tokenach używanych przez backend po zaufanym kanale. Wiele nowoczesnych bibliotek OIDC dla SPA oferuje już sprawdzone wzorce przechowywania i odświeżania tokenów.

    Czy mogę użyć jednej biblioteki OAuth2/OIDC do SPA, API i MVC jednocześnie?

    Najczęściej nie jest to dobry pomysł. Biblioteki są zwykle projektowane pod konkretny typ aplikacji: inne są potrzeby SPA działającego w przeglądarce, inne API, a jeszcze inne klasycznego MVC. Próba „upchnięcia” biblioteki napisanej pod SPA w aplikacji server-side kończy się zwykle obejściami i problemami z sesją lub przekierowaniami.

    Praktycznie: stosuje się jedną bibliotekę klienta OIDC w SPA, oddzielną konfigurację middleware OAuth2/JWT po stronie API oraz osobną konfigurację logowania w aplikacji MVC. Wszystkie łączy wspólny dostawca tożsamości (IdP) i wspólna konfiguracja scopes/claims.

    Jak podłączyć logowanie przez Google, Azure AD lub Keycloak bez przepisywania aplikacji?

    Kluczowe jest oparcie się na standardach OAuth2 i OpenID Connect, a nie na „sztywnym” SDK konkretnego dostawcy. Jeśli korzystasz z biblioteki OIDC, najczęściej wystarczy zmienić konfigurację: adres serwera autoryzacji (issuer/authority), client_id, sekrety oraz zakresy (scopes). Sam kod logowania może pozostać niemal taki sam.

    Dzięki temu możesz dziś używać np. Keycloak, jutro Azure AD, a pojutrze zewnętrznego IdP SaaS – bez wywracania całej aplikacji. Różnice sprowadzają się wtedy głównie do sposobu rejestracji aplikacji u dostawcy, konfiguracji redirect URI oraz ewentualnych dodatkowych claimów w tokenach.

    Co oznaczają podstawowe pojęcia: client, resource server, scope, claim, access token, ID token?

    W typowej konfiguracji: client to Twoja aplikacja (SPA, backend, MVC), która chce uzyskać dostęp do zasobów. Resource server to API, które chroni dane i weryfikuje access token. Authorization server / IdP (np. Keycloak, Azure AD) wydaje tokeny i zarządza logowaniem.

    Scope określa, o jakie uprawnienia prosi aplikacja (np. openid, profile, email, api.read). Claim to pojedyncza informacja w tokenie, np. sub (identyfikator użytkownika), email, role, exp (czas wygaśnięcia). Access token służy do wywoływania API, a ID token zawiera dane o zalogowanym użytkowniku i pozwala zbudować sesję po stronie klienta.

    Najważniejsze punkty

  • OAuth2 rozwiązuje problem bezpiecznego delegowania dostępu do API (kto i w czyim imieniu może coś zrobić), a OpenID Connect dodaje do tego jasne uwierzytelnianie użytkownika – dzięki temu nie trzeba tworzyć własnych, kruchych mechanizmów logowania.
  • Kluczowa różnica: OAuth2 to autoryzacja (uprawnienia), a OIDC to uwierzytelnianie (tożsamość użytkownika). Mieszanie tych ról prowadzi do niejednoznacznych, trudnych w utrzymaniu integracji, zwłaszcza gdy OAuth2 jest używany jako „pseudo‑logowanie”.
  • Dojrzałe biblioteki OAuth2/OIDC obsługują cały proces logowania i wylogowania (przekierowania, sesje, tokeny) oraz bezpieczne dołączanie i weryfikację tokenów przy wywołaniach HTTP, co znacząco zmniejsza ryzyko błędów bezpieczeństwa.
  • Dobór biblioteki jest ściśle związany z architekturą: inne narzędzia stosuje się w klasycznym MVC po stronie serwera, inne w SPA + API, a jeszcze inne w aplikacjach mobilnych. Próba użycia „uniwersalnej” biblioteki zwykle kończy się obejściami i walką z frameworkiem.
  • Identyczne mechanizmy OAuth2/OIDC można wykorzystać zarówno do SSO między wieloma aplikacjami, jak i do logowania zewnętrznego (Google, Azure AD, GitHub) czy integracji z własnym serwerem tożsamości – zmienia się głównie konfiguracja dostawcy, nie logika w aplikacji.
  • Podstawowe pojęcia (client, resource owner, authorization server/IdP, resource server, scope, claim, access/ID/refresh token) to fundament poprawnej konfiguracji; ich zrozumienie ogranicza błądzenie po dokumentacji i losowe „strzelanie” w ustawieniach.