Przejdź do głównej zawartości

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

ZmiennaDomyślnieZnaczenie
SKIP_AUTHtrue włącza obejście
SKIP_AUTH_EMAILdev@localhoste-mail użytkownika wyszukiwanego w lokalnym D1
SKIP_AUTH_USER_IDdev-mock-userid sesji, gdy nie ma dopasowania w bazie
SKIP_AUTH_ROLEADMINrola sesji zastępczej
SKIP_AUTH_CITYOpolepreferred_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:

  1. middleware.ts — przepuszcza request bez sprawdzania ciasteczka sesji i ustawia tenant-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/login renderowało się bez własnych tłumaczeń (MISSING_MESSAGE: auth). Pilnuje tego __tests__/middleware.skip-auth-public.test.ts.
  2. lib/session.tsgetServerSession() zwraca sesję zbudowaną przez getDevMockSession() zamiast pytać Better Auth.
  3. app/api/auth/[...all]/route.ts — odpowiada tą samą sesją zastępczą na GET /api/auth/get-session.
  4. lib/auth-client.ts — w przeglądarce poza produkcją kieruje authClient na window.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.ts przy SKIP_AUTH=true nie sprawdza route_access; sprawdzenie na poziomie strony (getIsRouteAllowed) działa normalnie, na podstawie roli z sesji zastępczej.