Skip to content

Dashboard Auth and Project Flow

Projects dashboard for authenticated project selection and membership

Projects dashboard for authenticated project selection and membership.

How dashboard authentication, token refresh, and project selection work together.

Why Auth State Is Split

The dashboard keeps the access token in memory and the refresh token in localStorage. This reduces persistent exposure of the short-lived access token while still allowing page reloads to restore a session.

Project selection is managed separately because project context is not the same as user identity. A user can stay logged in while switching the project used by specs, runs, requirements, credentials, schedules, and dashboard data.

sequenceDiagram
  participant Page
  participant AuthProvider
  participant Backend
  participant ProjectProvider
  Page->>AuthProvider: fetchWithAuth()
  AuthProvider->>Backend: request with access token
  Backend-->>AuthProvider: 401 when expired
  AuthProvider->>Backend: POST /auth/refresh
  Backend-->>AuthProvider: new access and refresh tokens
  AuthProvider->>Backend: retry original request
  ProjectProvider-->>Page: selected project id

Auth Runtime

web/src/contexts/AuthContext.tsx owns the browser auth runtime:

Concern Behavior
Access token Stored in module memory, not localStorage
Refresh token Stored in localStorage as refresh_token
Refresh timing Scheduled before the expected access-token expiry
Refresh races A shared refresh promise prevents concurrent refresh attempts
Authenticated requests fetchWithAuth retries once after a successful refresh
Logout Calls /auth/logout when possible, clears local refresh token and in-memory access token

Feature code should use fetchWithAuth for backend requests that may require auth. It should not read or write tokens directly.

Project Runtime

web/src/contexts/ProjectContext.tsx owns selected project state. Pages should use the project context or project-aware helpers instead of inventing page-local project storage.

Project-scoped API requests should include the selected project ID in the way the backend endpoint expects, usually as project_id query parameter or path segment. New pages should follow nearby feature pages rather than creating a new convention.

Provider Ordering

The dashboard shell should keep auth and project providers outside feature pages. Feature components can then assume:

  • auth state is loaded before protected content renders
  • project state is available through a hook
  • API helpers can attach auth headers consistently
  • redirect behavior stays centralized in auth components

Failure Modes

Failure Expected handling
Missing refresh token on mount Treat the user as logged out
Refresh request fails Clear refresh token and require login
Backend returns 401 Attempt one refresh, then return the failed response if refresh fails
Project is missing Fall back to the default project or show project selection depending on the page
User lacks project role Let backend authorization decide and surface the error in the page

Security Notes

  • Do not persist access tokens in page state, query strings, or logs.
  • Do not send refresh tokens to frontend API routes unless the route is explicitly part of auth.
  • Do not duplicate token refresh logic in feature pages.
  • Prefer backend permission checks over frontend-only hiding of controls.