The Workforce OS uses a secure JWT-based authentication mechanism designed for long-lived sessions while minimising the risk of credential exposure. Every protected API route requires a valid, unexpired access token; the refresh token mechanism lets the frontend silently renew sessions without interrupting the user’s workflow.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Paramount-Intelligence/HR_Monitoring_System/llms.txt
Use this file to discover all available pages before exploring further.
Token Design
Two token types are issued on every successful login:| Token | Lifetime | Purpose |
|---|---|---|
| Access Token | 15 minutes (access_token_expire_minutes) | Sent as Authorization: Bearer on every API request. Contains user_id, role, and type: access. |
| Refresh Token | 7 days (refresh_token_expire_days) | Stored in the database and linked to the user. Used only to obtain a new access token. |
HS256 using APP_SECRET_KEY. Access tokens embed sub (user UUID) and role so the backend can resolve permissions without an extra database lookup on every request.
Token lifetimes and rate-limit windows are all configurable via environment variables. The defaults shown here match the values in
apps/api/app/core/config.py.Login Flow
Backend Validation
The backend looks up the user by email and uses
verify_password() (BCrypt via Passlib) to compare the submitted password against the stored password_hash. If the credentials are wrong — or if the account is suspended or inactive — the request is rejected.Token Generation
On success, The refresh token’s
create_access_token(user_id, role) and create_refresh_token(user_id) are called:jti (JWT ID) is persisted to the database so it can be revoked on logout or role change.Token Refresh Flow
Trigger
The access token has expired or is detected as about to expire. The frontend intercepts the
401 response (or proactively refreshes before expiry).Backend Validation
decode_refresh_token() verifies the signature and expiry. The backend then checks that the token’s jti exists in the database and has not been revoked.Issue New Tokens
A new access token is issued. Optionally, a rotated refresh token (new
jti, same family_id) is returned and the old one is invalidated.Account Invitation & Activation Flow
Admin or HR Creates the User
An Admin or HR/Ops user creates a new account. The record is saved with
status=invited and an invitation email containing a unique, time-limited activation token is dispatched via the Celery email worker.User Clicks the Activation Link
The email links to the frontend activation page (
/activate?token=<activation_token>). This route is public and must not enforce the AuthContext auth guard — the user does not yet have a session.User Sets Password
The activation page prompts for a new password. On submission, the frontend calls the backend activation endpoint:The backend verifies the token, hashes the password with BCrypt, sets
status=active, and invalidates the activation token.Password Reset Flow
Request Reset
The user enters their email on the Forgot Password page, which calls
POST /api/v1/auth/forgot-password. The backend generates a PasswordResetToken and sends a reset email. The response is intentionally vague to prevent user enumeration.Submit New Password
The user follows the reset link (
/reset-password?token=...) and submits a new password via POST /api/v1/auth/reset-password. The backend validates the token, hashes the new password, and persists the update.Security States
Suspended / Inactive Users If a user’sstatus is set to suspended or inactive, all authentication attempts will fail. Any existing access tokens will appear valid until they expire, but the next refresh attempt will be rejected — effectively invalidating the session within 15 minutes.
Role Changes
Roles are baked into the access token payload at login time. If a user’s role is changed on the backend, the change will not reflect in the frontend until the next successful token refresh or re-login. Plan permission-sensitive role changes accordingly.
Logout
POST /api/v1/auth/logout invalidates the refresh token’s jti in the database and instructs the frontend to clear all local state (cookies, in-memory token).
Frontend Auth Guard
TheAuthContext exposes two protection mechanisms:
hasPermission(perm: string) → boolean
A helper function that checks whether the currently authenticated user’s role includes the given permission key (as defined in ROLE_PERMISSIONS). Use this for conditional rendering — showing or hiding buttons, form fields, and menu items.
RoleGuard Component
A higher-order component or layout wrapper that evaluates the user’s role on mount. It handles two redirect cases:
- Unauthenticated user accessing a dashboard route → redirect to
/login. - Authenticated user accessing a route restricted to a higher role → redirect to
/unauthorized.
AuthContext to dynamically render only the links the user is permitted to access.
Rate Limiting
The following limits are enforced server-side to prevent brute-force attacks:| Endpoint | Max Attempts | Window |
|---|---|---|
POST /api/v1/auth/login | 5 attempts | 15 minutes (900 s) |
POST /api/v1/auth/forgot-password | 3 attempts | 15 minutes (900 s) |
POST /api/v1/auth/reset-password | 5 attempts | 15 minutes (900 s) |
auth_login_max_attempts, auth_forgot_password_max_attempts, and auth_reset_password_max_attempts in apps/api/app/core/config.py and can be overridden via environment variables.
Debugging Auth Issues
- 403 Forbidden
- Network Error on Login
- Redirect Loop
