Skip to main content

Authentication and Session

Introduction​

This document describes the frontend authentication flow, cookie session storage, route protection, permission handling, and logout behavior.


Login Flow​

The login page is src/pages/LoginPage.tsx.

High-level flow:

  1. Credentials: User submits email and password.
  2. API Call: The page calls api.auth.login(credentials).
  3. Data Extraction: On success, the page extracts token, user.id, and user.organizationId.
  4. Session Storage: It calls auth.setAuth(...).
  5. Navigation: It shows success feedback and navigates to /dashboard.

The expected login response is typed as:

interface AuthResponse {
token: string;
user: User;
}

The frontend expects the backend to return this response inside the standard API wrapper:

interface ApiResponse<T> {
success: boolean;
data: T;
errors: string[];
traceId?: string | null;
}

Session storage is managed by src/lib/auth.ts with js-cookie.

Cookie KeySource ConstantPurpose
access_tokenCOOKIE_KEYS.ACCESS_TOKENBearer token for API calls
user_idCOOKIE_KEYS.USER_IDCurrent user ID
organization_idCOOKIE_KEYS.ORGANIZATION_IDCurrent organization scope
user_nameCOOKIE_KEYS.USER_NAMEReserved constant, not part of core auth write flow
user_emailCOOKIE_KEYS.USER_EMAILReserved constant, not part of core auth write flow

Cookie options:

OptionValue
securetrue when current page protocol is HTTPS
sameSitestrict
expires7 days

Route Protection​

PrivateRoutes in src/app/PrivateRoutes.tsx protects all /dashboard routes.

The guard uses only this check:

auth.isAuthenticated()

isAuthenticated() returns true when access_token exists in cookies. Token validity is enforced by the backend on API calls, not by decoding the token client-side.


API Authentication​

src/lib/api.ts creates a centralized Axios instance. The request interceptor reads auth.getToken() and attaches:

Authorization: Bearer <token>

The response interceptor handles HTTP 401:

  1. Clear auth cookies.
  2. Redirect the browser to /.
  3. Reject the original request promise.

The analytics chat streaming client in src/lib/analyticsChat.ts uses fetch rather than Axios, but follows the same token rule and clears the session on 401.


Current User and Permissions​

src/hooks/useApi.ts exposes useUserPermissions().

The hook:

  1. Checks auth.isAuthenticated().
  2. Calls api.auth.me().
  3. Reads permissions from user.role.permissions.
  4. Exposes hasPermission(slug).

Permission slugs are centralized in src/lib/constants.ts:

DomainSlugs
Sensorscreate-sensor, update-sensor, delete-sensor, read-sensor
Userscreate-user, edit-user, delete-user, read-user
Rolescreate-role, edit-role, delete-role, read-role
Organizationedit-organization

Feature screens use these slugs to show or hide create, edit, and delete actions. The backend remains responsible for final authorization enforcement.


Logout Flow​

Logout is triggered from the dashboard shell user menu.

Flow:

  1. Confirmation: Show a confirmation dialog through showConfirm.
  2. API Call: Call api.auth.logout().
  3. Session Clearing: Clear auth cookies with auth.clearAuth().
  4. Feedback: Show a success message.
  5. Navigation: Navigate to /.

If the backend logout call fails, the frontend still clears local auth data and navigates to /.


Session Failure Modes​

ScenarioFrontend Behavior
Missing token before entering dashboardRedirect to /
API returns 401Clear cookies and redirect to /
/auth/me fails in permission hookPermission list becomes empty
Logout API failsLocal auth is still cleared
User ID cookie missing in shellShell skips current user fetch

Security Notes​

  • The frontend stores bearer tokens in cookies, not in localStorage.
  • Cookies are sameSite: strict, reducing cross-site request risk.
  • The secure flag depends on serving the app over HTTPS.
  • The frontend does not implement token refresh.
  • The frontend does not decode JWT claims; it relies on backend endpoints for current user and permissions.