Skip to main content

Sessions and the website hand-off

Sign-in paths​

PathHow
Email + password on /loginPOST /rest/api/token/
Google on /loginGoogleButton posts the ID token to POST /rest/auth/google/
From the website/auth/handoff exchanges a one-time code (below)
Sign-upRedirects to the website's /account/sign-up?next=app when VITE_WEBSITE_URL is set, so the website owns account creation and hands the new account straight back

The hand-off​

src/pages/HandoffPage.jsx renders before the auth gate in src/App.jsx, and bootstrapAsync skips its usual refresh on that path. Expired or replayed links show a toast and land on /login. The code travels in the URL fragment, never a query string, so it never reaches a server log.

The backend only honours exchanges from origins in APP_ALLOWED_ORIGINS (+ APP_EXTRA_ORIGINS). Contract: docs/WEBSITE_ACCOUNTS.md.

Refresh and logout​

src/store/AxiosInstance.jsx (withCredentials: true):

  • On 401: refresh with the body token (POST /rest/api/token/refresh/) and retry once; if that fails, log out.
  • On 429: show a toast.
  • Sign-in requests pass {skipAuthRefresh: true}.

Logout calls POST /rest/api/token/logout/ so the refresh token is blacklisted server-side.

Deploy order​

When the hand-off contract changes: backend, then this app, then the website.