Download the PHP package musikhood/auth-client-bundle without Composer
On this page you can find all versions of the php package musikhood/auth-client-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package auth-client-bundle
auth-client-bundle
Symfony bundle dla paneli, które delegują uwierzytelnianie do centralnego auth servera:
logowanie, 2FA, refresh, wylogowanie, JWT/JWKS,
ciasteczka HttpOnly (BEARER + refresh_token) i introspekcja sesji z circuit
breakerem. Front (@musikhood-dev/auth-client) nigdy nie widzi JWT.
Od 0.8 paczka działa bez konfiguracji: integrator podaje tylko dane panelu
w env, a auth-client:install + auth-client:doctor robią resztę.
Pełna checklista podpięcia panelu (admin → backend → front → 2FA → problemy):
INTEGRATION.md.
Agent AI podpinający panel: AI_INTEGRATION.md (w paczce:
vendor/musikhood/auth-client-bundle/AI_INTEGRATION.md;auth-client:installiauth-client:doctorwypisują tę ścieżkę).
Quick start (5 minut)
- Admin tworzy panel w panelu administracyjnym auth servera (origin frontu, webhook URL = URL backendu panelu) i zapisuje
AUTH_BASE_URL,panel_id,client_id,client_secretw bazie dostępów Work HUB. -
Instalacja paczki:
- Adres auth servera i dane panelu do
.env.local(lokalnie) / sekretu k8s — z bazy dostępów Work HUB (secret_to_env):AUTH_BASE_URL(np.https://auth.example.com),AUTH_PANEL_ID,AUTH_CLIENT_ID,AUTH_CLIENT_SECRET. Wszystkie cztery są wymagane — bundle nie ma wbudowanego adresu auth servera (od 0.8.1). -
Konfiguracja security, CORS, tras i trusted proxies (pokazuje zmiany i pyta przed zapisem):
-
Weryfikacja — ma być zielono:
- Front:
npm i @musikhood-dev/auth-client@^2.3.1inpx @musikhood-dev/auth-client init(README paczki).
Gotowe. Nie potrzebujesz encji User, tabeli ani migracji (tryb bezstanowy). Polityka 2FA
(off / optional / required) i device binding — wyłącznie w panelu administracyjnym auth servera.
Endpoint Flex jest opcjonalny. Bez niego Flex i tak rejestruje bundle w
config/bundles.php, aauth-client:installdopisuje import tras i kluczeAUTH_*do.env. Jeśli chcesz recipe (gotowyauth_client.yamlz 4 liniami env i komunikat po instalacji), raz w projekcie:
Jak działa
- Logowanie: front woła
/api/loginw backendzie panelu; bundle forwarduje do auth servera (/api/auth/backend/login) i ustawia ciasteczka HttpOnly. Kroki 2FA (mfa_required,mfa_enrollment_required, step-up) przechodzą do frontu 1:1. - Każdy request:
JwtCookieAuthenticatorweryfikuje JWT offline (JWKS), aAuthValidationListenercovalidation_cache_ttl(30 s) pyta auth server, czy sesja żyje, czy user ma dostęp do TEGO panelu i czy spełnia politykę 2FA panelu. 401 = sesja martwa (front robi refresh), 403 = brak dostępu / 2FA. - User: domyślnie bezstanowy — budowany z JWT i introspekcji, snapshot w
cache.app(role zmienione w panelu administracyjnym docierają w ≤30 s). Opcjonalnie własna tabela userów („Lokalna kopia userów”). - Trasy publiczne (jedno źródło w kodzie:
Musikhood\AuthClient\Routing\PublicRoutes):/api/login,/api/login/mfa,/api/logout,/api/token/refresh,/api/token/invalidate,/api/session/mfa,/api/mfa/enrollment/request,/api/auth-client/webhook/user-invalidated. Reszta^/apiwymaga zalogowania.
Komendy
bin/console auth-client:install
Opcje: --dry-run (tylko pokaż), --yes (zapisz bez pytania), --no-doctor.
| Plik | Co robi |
|---|---|
.env |
dopisuje puste AUTH_BASE_URL i AUTH_PANEL_ID/CLIENT_ID/CLIENT_SECRET (z komentarzem o bazie dostępów) i SYMFONY_TRUSTED_PROXIES=private_ranges — tylko klucze, których nie ma w .env ani w środowisku (.env.local, sekrety) |
config/routes/auth_client.yaml |
import tras bundla (jeśli nigdzie go nie ma) |
config/packages/security.yaml |
brak pliku / domyślny plik Symfony → pełna konfiguracja (sekcje when@* zachowane); własne firewalle → nic nie nadpisuje: sprawdza pokrycie tras bundla (kolejność jak w Symfony) i jeśli czegoś brakuje, wypisuje dokładny fragment do wklejenia |
config/packages/nelmio_cors.yaml |
(jeśli jest NelmioCorsBundle) allow_credentials: true, allow_headers += X-Device-Fp, expose_headers += Retry-After; plik, którego nie da się bezpiecznie edytować → fragment do wklejenia; brak Nelmio → instrukcja |
config/packages/framework.yaml |
tylko Symfony < 7.2: trusted_proxies: '%env(default::SYMFONY_TRUSTED_PROXIES)%' (7.2+ czyta env sam) |
Na końcu uruchamia auth-client:doctor (po zapisie — w nowym procesie, z nową konfiguracją).
Drugie uruchomienie nic nie zmienia.
bin/console auth-client:doctor
Sprawdza i wypisuje ✔ / ! / ✘ z podpowiedzią, co zrobić (exit code ≠ 0 przy błędzie — nadaje się do CI / smoke testu po deployu). Nigdy nie wypisuje sekretów.
- konfiguracja: env ustawione i niepuste,
panel_idto UUID; - auth server osiągalny (
/.well-known/jwks.json), poprawneclient_id/client_secret(introspekcja bez tokenu: zły Basic = 401 zWWW-Authenticate), auth server ma trasy 2FA; panel_idzgodny z klientem — z--access-token=<wartość ciasteczka BEARER>(introspekcja prawdziwej sesji);- security: wszystkie trasy publiczne bez logowania,
^/apizaJwtCookieAuthenticatoriIS_AUTHENTICATED_FULLY(na żywej konfiguracji, pocache:clear); - CORS (Nelmio): credentials,
X-Device-Fp,Retry-After, niepustyallow_origin; - ciasteczka:
same_site=nonebezsecure= ostrzeżenie (przeglądarka odrzuci ciasteczko);noneprzy froncie w tej samej domenie co API = podpowiedź „lax wystarczy” (gdy da się ustalić origin frontu); info o domenie; - trusted proxies (IP usera dla historii logowań i limitu prób);
- wersja bundla.
Wymagania
- PHP
>=8.2, Symfony^6.4 || ^7.0(framework-bundle, security-bundle, http-client, console) cache.app(zalecany Redis — snapshot userów, tokenVersion z webhooka, circuit breaker)- Auth server z trasami 2FA (
/api/auth/backend/login/mfaitd.) — doctor sprawdza - Opcjonalnie:
nelmio/cors-bundle, gdy front działa pod innym originem niż API
Instalacja ręczna (bez auth-client:install)
security.yaml — dokładnie to, co generuje install (SecurityConfigurator::template()):
Trasy: config/routes/auth_client.yaml z resource: '@AuthClientBundle/Resources/config/routes.yaml'.
CORS i trusted proxies — jak w tabeli auth-client:install wyżej.
Opcjonalnie: lokalna kopia userów (encja + tabela)
Potrzebne tylko, gdy aplikacja chce trzymać userów u siebie (np. relacje w bazie,
wyszukiwanie po e-mailu w zapytaniach). Gdy kontener ma implementację
PanelUserRepositoryInterface, bundle używa jej zamiast trybu bezstanowego —
bez żadnej innej zmiany konfiguracji.
Encja User
Paczka nigdy nie pisze do tabeli użytkowników — to robi konsument.
Zaimplementuj Musikhood\AuthClient\Contract\PanelUserInterface
(referencja: docs/example-entity.php):
Jeśli używasz innej lokalizacji niż src/Entity/ (np. DDD ze strukturą
src/Domain/User/Entity/User.php), umieść klasę gdziekolwiek — paczka
patrzy tylko na kontrakt PanelUserInterface.
UserRepository
Zaimplementuj Musikhood\AuthClient\Contract\PanelUserRepositoryInterface
i powiąż go z interfejsem przez atrybut #[AsAlias]. Dzięki temu nie
musisz nic dopisywać do services.yaml — Symfony sam podepnie repo pod
interfejs.
Referencja: docs/example-repository.php.
Atrybut #[AsAlias] wymaga Symfony 6.1+. Jeśli z jakiegoś powodu wolisz
mapowanie w YAML-u, zamiast atrybutu dodaj do config/services.yaml:
Bez aliasu bundle używa trybu bezstanowego (od 0.8 — wcześniej rzucał wyjątek).
Tabela w bazie
Albo skopiuj docs/example-migration.sql
prosto do swojego narzędzia migracji, albo wygeneruj migrację z encji:
Tabela potrzebuje kolumn: id (UUID), email (unique), display_name,
roles_for_panel (JSON), disabled (bool), last_synced_at. Pełny
mapping w docs/example-entity.php.
Synchronizacja z auth serverem
Auth server jest jedynym źródłem prawdy dla ról, displayName i flagi
disabled. Kopia użytkownika po stronie panelu — w trybie bezstanowym snapshot
w cache.app, w trybie z lokalną tabelą wiersz w bazie — jest aktualizowana
w dwóch momentach:
- Pierwszy kontakt z userem (bootstrap) — gdy authenticator widzi zalogowanego usera, którego jeszcze nie ma w lokalnej tabeli, paczka tworzy kopię z claimów świeżego JWT (email, displayName, role per-panel). To jednorazowe — przy każdym kolejnym requeście tego usera authenticator NIE rusza już lokalnej kopii.
- Co ~30s na żądanie zalogowanego usera —
AuthValidationListenerwoła introspekcję na auth serverze (POST /api/auth/backend/introspect) i synchronizuje pełen payload (email, displayName, role per-panel, flagadisabled). To jest jedyna ścieżka aktualizacji istniejącej kopii. Krok pomijany jeśli wynik z poprzedniego wywołania jeszcze leży w cache (validation_cache_ttl, domyślnie 30s).
W szczególności paczka nie używa lokalnej flagi isDisabled() do
podejmowania decyzji o autoryzacji. Gating disabled userów leci
wyłącznie przez /me — co znaczy że:
- Po zablokowaniu konta w panelu admin auth servera użytkownik
zostanie wylogowany w czasie max.
validation_cache_ttlsekund. - Po odblokowaniu konta użytkownik znowu działa bez ponownego
logowania (jeśli JWT jest jeszcze ważny — w innym przypadku front
interceptor zrobi
/api/token/refreshi auth server wystawi nowy). - Po zmianie ról / displayName w panelu admin nowe wartości pojawią się
w lokalnej kopii w czasie max.
validation_cache_ttlsekund. Auth server NIE podbijatokenVersionprzy tych zmianach — istniejące JWT zostają ważne, propagacja idzie przez/me.
Lokalne pole disabled (w encji konsumenta albo w snapshot'cie trybu
bezstanowego) służy tylko do wyświetlenia. Aktualizowane automatycznie przez
syncFromMe().
Tryb bezstanowy przechowuje snapshot w cache.app (TTL 24 h) — utrata cache
jest nieszkodliwa (user zostaje odtworzony z JWT, a następna introspekcja go
zsynchronizuje). Zalecany Redis, żeby workery widziały ten sam stan.
Webhook inwalidacji (0s revocation)
Od wersji 0.3.0 paczka nasłuchuje webhooków od auth servera i skraca czas
rewokacji sesji z ~30s (poll /me opisany wyżej) do setek milisekund.
Jak to działa. Po inwalidacji usera (zablokowanie konta, zmiana hasła,
odebranie dostępu do panelu) auth server podbija tokenVersion i pushuje
podpisany webhook na endpoint paczki
POST /api/auth-client/webhook/user-invalidated. Paczka weryfikuje podpis
(WebhookJwtValidator, ten sam JWKS co user JWT), zapisuje nową tokenVersion
w cache (UserTokenVersionStore) i kasuje cache walidacji usera. Przy
najbliższym requeście tego usera JwtCookieAuthenticator porównuje ver
z jego JWT z zapisaną wartością i odrzuca stary token (401) bez czekania na
poll /me.
Co musisz zrobić.
- Ścieżka webhooka musi być publiczna —
auth-client:installumieszcza ją w firewallulogin(lista tras publicznych:Musikhood\AuthClient\Routing\PublicRoutes). Webhook autoryzuje się sam podpisem JWT auth servera — to model jak weryfikacja podpisu webhooków Stripe/GitHub, nie dziura w security. - W panelu admin auth servera ustaw pole „Webhook URL" dla swojego panelu na
bazowy URL backendu mikroserwisu (np.
https://api.example.com). Auth server sam dokleja ścieżkę/api/auth-client/webhook/user-invalidated. - Upewnij się, że
cache.appjest skonfigurowane (Redis zalecany — stan musi być współdzielony między procesami workerów i przeżyć restart).
Nie musisz nic zmieniać w swojej encji. tokenVersion żyje w cache PSR,
nie w kolumnie DB — webhook może przyjść nawet dla usera, którego mikroserwis
jeszcze nie zna lokalnie. Reset cache (flush Redisa) jest nieszkodliwy: brak
wpisu = pass, a poll /me dogoni inwalidację w ~30s (fallback).
Monolog tip. Jeśli używasz fingers_crossed z action_level: error
(typowy prod default Symfony), logi webhook.received (poziom info) będą
buforowane i tracone, gdy webhook kończy się 200 OK — bufor jest zrzucany
tylko gdy w tym samym requeście wystąpi error. Żeby widzieć je w
kubectl logs, dodaj osobny handler/channel ze stream do php://stderr
na poziomie info.
Kontrakt z front-endem
Front nigdy nie widzi JWT. Wymaga:
axios.defaults.withCredentials = true(lub równowartośćfetchzcredentials: 'include')- na
401z dowolnego/api/*:POST /api/token/refresh, potem retry - na
401z/api/token/refresh: czyść lokalny stan UI, redirect do logowania - na
403z dowolnego/api/*(brak dostępu do tego panelu, sesja ŻYJE): NIE refreshuj, NIE czyść ciasteczek, NIE broadcastuj wylogowania — pokaż ekran „brak dostępu" / login. Ciasteczka SSO zostają ważne na panelach, do których user MA dostęp. Paczka@musikhood-dev/auth-clientklasyfikuje to jakoPanelAccessDeniedErrorautomatycznie.
Kontrakt 401 vs 403: 401 = sesja martwa (globalne wylogowanie po nieudanym refreshu), 403 = brak dostępu do panelu (sesja żyje, bez czyszczenia).
To ten sam kontrakt co przy gadaniu wprost z auth serverem, więc istniejący front przesiada się na inny backend bez zmian.
Pełna konfiguracja
Plik config/packages/auth_client.yaml jest opcjonalny — wszystkie klucze
mają domyślne wartości (dane panelu z env). Recipe wgrywa tylko cztery linie
z env, żeby było widać, skąd biorą się dane. Pełny dump:
bin/console config:dump-reference auth_client.
same_site domyślnie lax — wystarcza, gdy front i API są w tej samej domenie rejestrowalnej (SameSite liczy
się po „site”, nie po hoście): panel.example.com + panel-api.example.com, localhost na różnych portach.
none ustaw tylko, gdy front i API są w różnych domenach (np. panel.example.com ↔ api.example.org) —
wymaga secure: true (HTTPS) i osłabia ochronę przed CSRF.
API token (autoryzacja maszynowa)
Drugi sposób autoryzacji obok ciasteczek — dla klientów server-to-server, które
nie mają sesji przeglądarkowej. Klient wysyła per-user-per-panel token w
nagłówku X-Api-Token (format mhpat_…), bez logowania, ciasteczek ani
refreshu.
ApiTokenAuthenticator reaguje tylko gdy nagłówek jest obecny, więc współistnieje
z JwtCookieAuthenticator w tym samym firewallu — bez nagłówka stary cookie flow
działa bez zmian. Token jest weryfikowany live przez introspekcję na auth serverze
(POST /api/auth/backend/api-token/verify) — paczka nie trzyma kopii, więc revoke
po stronie auth servera = natychmiastowa rewokacja (następny request → 401). Gdy
auth server jest niedostępny, authenticator robi fail-closed (401).
Role pochodzą z panelRoles introspekcji (z prefiksem ROLE_), tak samo jak przy
JWT — istniejące #[IsGranted('ROLE_PUBLISH')] działają bez zmian. Token jest
generowany / regenerowany / usuwany wyłącznie przez admina w panelu auth servera
(„niezmienny" = brak edycji wartości; zmiana = regeneracja, stary natychmiast
martwy).
2FA (TOTP) i wiązanie sesji z urządzeniem (>= 0.7)
Wymaga auth servera z wydaniem 2FA. Front: @musikhood-dev/auth-client >= 2.2.
Panel z polityką 2FA off (domyślnie) i user bez 2FA = zachowanie bajt w bajt jak w 0.6.
/api/loginprzepuszcza 401{code: "mfa_required", mfa_token, methods}(BEZ ciasteczek) i 403{code: "mfa_enrollment_required", enrollment_url}; zwykłe złe dane = 401"Nieprawidłowy login lub hasło."(+code: invalid_credentials). 429too_many_attempts+Retry-After.- Per request (
AuthValidationListener): introspekcja zwraca 403mfa_step_up/mfa_enrollment_required, gdy sesja nie spełnia polityki 2FA panelu — bundle odpowiada 403 z tym samymcode(bez czyszczenia ciasteczek, wynik nie jest cache'owany). 403mfa_enrollment_requiredz bramki NIE zawiera linku — front prosi o hasło i woła/api/mfa/enrollment/request(link jest tylko w odpowiedzi/api/login). - Dane end-usera dla auth servera: przy
/api/auth/backend/*bundle wysyłaX-Client-Ip(Request::getClientIp()— skonfigurujframework.trusted_proxies!),X-Client-User-Agent,X-Device-Fporazdevicez body przeglądarki. Auth server ufa im wyłącznie po poprawnym HTTP Basic panelu. Bez nich (stary bundle) historia logowań widzi IP serwera konsumenta, a auth server pomija dla takich wywołań limit prób per IP (zostaje limit IP+email) — inaczej userzy jednego serwisu blokowaliby się nawzajem. - Fingerprint:
JwtCookieAuthenticatorporównuje claimfphz nagłówkiemX-Device-Fpwgauth_client.device_binding.mode:off|observe(default, tylko log) |enforce(401device_mismatch_recheck— także przy braku nagłówka na tokenie zfph→ front robi refresh; auth server robi pełny scoring). Refresh odrzucony zdevice_mismatch→ 401 + czyszczenie ciasteczek. - CORS backendu konsumenta musi dopuszczać nagłówek
X-Device-Fp(allow_headers) i wystawiaćRetry-After(expose_headers) — auth-client 2.2 wysyłaX-Device-Fpna każdym requeście. Od 0.8 robi toauth-client:install. - Firewall: trasy 2FA są w publicznym firewallu
login— od 0.8 robi toauth-client:install, aauth-client:doctorsprawdza pokrycie na żywej konfiguracji. - Polityka 2FA i device binding ustawia się wyłącznie w panelu administracyjnym auth servera (panel:
mfa_policy,device_binding).device_binding.modew bundlu zostawobserve. - Nowe wyjątki
AuthBackendClient(podklasy dotychczasowych — stary kod łapiącyAuthBackendUnauthorizedException/AuthBackendForbiddenExceptiondziała fail-closed):AuthBackendMfaRequiredException,AuthBackendMfaEnrollmentRequiredException,AuthBackendMfaStepUpException,AuthBackendMfaInvalidCodeException,AuthBackendMfaTokenExpiredException,AuthBackendDeviceMismatchException,AuthBackendMfaAlreadyEnabledException,AuthBackendTooManyAttemptsException. Nowe metody:loginMfa(),sessionMfa(),requestMfaEnrollment();login()/refresh()przyjmują opcjonalne$device.
Endpointy wystawiane przez paczkę
| Metoda | Ścieżka | Opis |
|---|---|---|
POST |
/api/login |
Wymienia {username, password} na ciasteczka BEARER + refresh_token. |
POST |
/api/logout (alias /api/token/invalidate) |
Czyści ciasteczka, unieważnia refresh token w auth serverze. |
POST |
/api/token/refresh |
Generuje nową parę ciasteczek z refresh tokena. |
GET |
/api/v1/user/me |
Zwraca dane zalogowanego użytkownika (id, email, displayName, roles, disabled). Czyta z lokalnej kopii — nie wymaga round-tripa do auth servera. |
POST |
/api/login/mfa |
(0.7) Drugi krok logowania 2FA: {mfa_token, code \| recovery_code, remember_device?, device?} → ciasteczka jak /api/login. Publiczny. |
POST |
/api/session/mfa |
(0.7) Step-up sesji po 403 mfa_step_up: {code \| recovery_code, remember_device?, device?} + ciasteczko refresh_token → nowe ciasteczka. Publiczny (autoryzacja refresh tokenem). |
POST |
/api/mfa/enrollment/request |
(0.7) Link do konfiguracji 2FA dla zalogowanego usera: {password} (wymagane aktualne hasło) + ciasteczko refresh_token → {enrollment_url}; 401 invalid_credentials, 409 mfa_already_enabled, 429. Publiczny (refresh token + hasło). |
POST |
/api/auth-client/webhook/user-invalidated |
Webhook inwalidacji usera (0s revocation). Autoryzacja podpisem JWT auth servera (nie user auth — wymaga PUBLIC_ACCESS w access_control). Patrz „Webhook inwalidacji". |
Licencja
MIT — patrz LICENSE.
All versions of auth-client-bundle with dependencies
ext-openssl Version *
firebase/php-jwt Version ^6.10 || ^7.0
psr/cache Version ^2.0 || ^3.0
psr/log Version ^2.0 || ^3.0
ramsey/uuid Version ^4.7
symfony/cache-contracts Version ^2.5 || ^3.0
symfony/config Version ^6.4 || ^7.0
symfony/console Version ^6.4 || ^7.0
symfony/dependency-injection Version ^6.4 || ^7.0
symfony/event-dispatcher Version ^6.4 || ^7.0
symfony/framework-bundle Version ^6.4 || ^7.0
symfony/http-client Version ^6.4 || ^7.0
symfony/http-client-contracts Version ^2.5 || ^3.0
symfony/http-foundation Version ^6.4 || ^7.0
symfony/http-kernel Version ^6.4 || ^7.0
symfony/routing Version ^6.4 || ^7.0
symfony/security-bundle Version ^6.4 || ^7.0
symfony/security-core Version ^6.4 || ^7.0
symfony/security-http Version ^6.4 || ^7.0
symfony/yaml Version ^6.4 || ^7.0