Pomijanie logowania na środowisku lokalnym (SKIP_AUTH)
Dev-only obejście ekranu logowania, żeby pracować nad panelem bez przechodzenia przez Better Auth.
👤 Dla kogo
Dokument techniczny (dla developerów). Opisuje, jak uruchomić aplikację lokalnie bez logowania, jakimi zmiennymi sterować rolą i kontem oraz dlaczego mechanizm nie może zadziałać na produkcji.
Jak używać
W .env.local:
SKIP_AUTH=true
Następnie yarn dev — wejście na /dashboard nie przekierowuje już na /auth/login.
Zmienne
| Zmienna | Domyślnie | Znaczenie |
|---|---|---|
SKIP_AUTH | – | true włącza obejście |
SKIP_AUTH_EMAIL | dev@localhost | e-mail użytkownika wyszukiwanego w lokalnym D1 |
SKIP_AUTH_USER_ID | dev-mock-user | id sesji, gdy nie ma dopasowania w bazie |
SKIP_AUTH_ROLE | ADMIN | rola sesji zastępczej |
SKIP_AUTH_CITY | Opole | preferred_city sesji zastępczej |
Jeżeli SKIP_AUTH_EMAIL wskazuje na istniejący wiersz w tabeli user, sesja jest budowana z prawdziwego rekordu — rola, specialRoleIds i miasto są takie same jak po zalogowaniu, więc route_access zachowuje się realistycznie. Gdy użytkownika nie ma (albo baza jest niedostępna), lib/auth-dev-mock.ts loguje ostrzeżenie i podstawia syntetyczny wiersz sterowany zmiennymi z tabeli powyżej.
Jak to działa
Obejście ma cztery punkty zaczepienia — dwa po stronie serwera i dwa po stronie przeglądarki:
middleware.ts— przepuszcza request bez sprawdzania ciasteczka sesji i ustawiatenant-id. Gałąź obejścia stoi za blokami stron publicznych, nie przed nimi: to one ustawiająx-public-page, po którym root layout wysyła publiczne namespace'y tłumaczeń. Kiedy obejście wracało wcześniej,/auth/loginrenderowało się bez własnych tłumaczeń (MISSING_MESSAGE: auth). Pilnuje tego__tests__/middleware.skip-auth-public.test.ts.lib/session.ts—getServerSession()zwraca sesję zbudowaną przezgetDevMockSession()zamiast pytać Better Auth.app/api/auth/[...all]/route.ts— odpowiada tą samą sesją zastępczą naGET /api/auth/get-session.lib/auth-client.ts— w przeglądarce poza produkcją kierujeauthClientnawindow.location.origin.
Punkt 2 jest kluczowy dla serwera: getServerSession() jest jedynym źródłem sesji dla getAuthSession(), lib/roles.ts, lib/utils/server-route-access.ts i app/layout.tsx, więc podmiana w tym miejscu obsługuje wszystkie warstwy naraz. Wcześniej (po migracji Auth0 → Better Auth, AP-650) mock nie był podpięty nigdzie — middleware przepuszczał request, ale strony i tak robiły własny redirect('/auth/login').
Punkty 3 i 4 domykają przeglądarkę. Bez nich strony renderowały się jako zalogowane, ale useSession() odpowiadało „wylogowany", więc żaden dialog się nie otwierał i znikały elementy adminowe — komponenty klienckie bramkowane useUser() nie miały użytkownika. Powód był podwójny: authClient czytał NEXT_PUBLIC_BASE_URL, które w .env.local wskazuje na wdrożony host, więc zapytanie o sesję szło cross-origin i wracało jako błąd CORS; a nawet po skierowaniu go na localhost prawdziwy handler Better Auth nie miał ciasteczka i odpowiadałby „brak sesji".
Zachowanie authClient jest przypięte testem w __tests__/lib/auth-client-base-url.test.ts: produkcyjny build zawsze używa NEXT_PUBLIC_BASE_URL.
Dlaczego to nie zadziała na produkcji
isDevAuthBypassEnabled() wymaga obu warunków:
process.env.SKIP_AUTH === 'true' && process.env.NODE_ENV !== 'production';
Buildy workerów są budowane z NODE_ENV=production, więc nawet ustawienie SKIP_AUTH=true w sekretach Cloudflare nie włączy obejścia. getDevMockSession() dodatkowo rzuca wyjątkiem, jeśli zostanie wywołane poza tą bramką. Zachowanie jest przypięte testami w __tests__/lib/auth-dev-mock.test.ts.
Ograniczenia
- Obejście nie tworzy wiersza w tabeli
session— funkcje zależne od prawdziwego tokenu sesji (np. handoff CLIENT ↔ ADMIN) trzeba testować na normalnym logowaniu. - Sesja zastępcza jest budowana przy każdym zapytaniu; wylogowanie i przelogowanie w UI nie mają sensu przy włączonej fladze.
middleware.tsprzySKIP_AUTH=truenie sprawdzaroute_access; sprawdzenie na poziomie strony (getIsRouteAllowed) działa normalnie, na podstawie roli z sesji zastępczej.