qo
Qovery
GitHub
console
Workspace
GitHub
CI Pipeline Executions
Current execution
Succeeded
fix/login-redirect-loop-on-stale-session
Runs and Agents
Configuration
Self-Healing CI
Project Graph
Circular Dependencies
Enterprise
Resource Usage
Analysis
Conformance
Enterprise
Runs
Current run
Succeeded
Tasks
Resource Usage
Insights
Compare tasks
Analytics
Sign in
Toggle sidebar
fix(auth): stop the /login redirect loop left by a dead Auth0 session What: When Auth0 cannot mint a token, the request interceptor now clears the local session before redirecting, and it tags the login URL with `reason=session-expired`. The login route skips its "already authenticated, go back into the app" redirect when that reason is present, and shows the user why they were signed out. Session teardown is single-flight across every axios instance. The duplicate `useAuthInterceptor` registration on the Auth0 callback route is removed. `getSafeRedirect` and the new login-URL contract move into `@qovery/shared/routes` so both sides share one definition. Why: `Auth0Provider` runs with `cacheLocation="localstorage"`, and the SDK reads the cached user back with no expiry check — `checkSession()` swallows the refresh failure. So `isAuthenticated` stays true for a session that can no longer produce a token, and nothing ever clears it. That closed a loop: at `/` a query fires, the interceptor fails to get a token and does `window.location.assign('/login?redirect=%2F')` (a full page reload), the reload restores the same dead session, `/login` sees `isAuthenticated` and redirects to `/`, and round it goes. The tab is unrecoverable without manually wiping localStorage. Introduced by #2727, which added the redirect but no teardown. Notes: Teardown is awaited before navigating: a reload that outruns the cache wipe restores the dead session and the loop survives. That ordering is covered by a test. A 401 response does NOT clear the session, only tags the URL. Because `getAccessTokenSilently` refreshes proactively, an expired token never reaches the API, so a 401 is as likely to be an endpoint using 401 where it means 403 — clearing there would sign out a healthy user. The reason param is what breaks the loop on that path. Single-flight matters in practice, not just in theory: two axios instances plus React Query's default three retries turn one dead session into a dozen concurrent teardown attempts. The reason param, not the teardown, is also the only guard that works for the `qovery-e2e-auth-token` bypass, which forces `isAuthenticated` true and is immune to `logout()`. `libs/shared/routes` had a jest transform that could not parse the shared TypeScript setup file, so its suite could never run; aligned it with `shared-utils`. Out of scope, worth a follow-up: `router.invalidate()` on auth change, moving `/`'s component-level <Navigate> into a beforeLoad guard, and memoising the Auth0 context value. None are required for this loop.
nx run console:build --parallel=3 --configuration=development
⌘K
Succeeded
nx run console:build --parallel=3 --configuration=development
Click to copy
read-write
access token used
cc56d854
2906