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.