Pro Login and Session Management

Summary

This process covers the full session lifecycle for a Pro: logging in with email/password, silently refreshing the access token via the httpOnly cookie, and logging out. Refresh tokens are rotated on every use (one active token per device type). Deactivated accounts are blocked at login.

State Diagram

stateDiagram-v2
    state "Déconnecté" as LoggedOut
    state "Authentification en cours" as Authenticating
    state "Compte bloqué" as Blocked
    state "Session active" as Active
    state "Renouvellement session" as Refreshing

    [*] --> LoggedOut
    LoggedOut --> Authenticating: pro submits credentials
    Authenticating --> Blocked: isActive = false
    Authenticating --> Active: credentials valid
    Active --> Refreshing: access token expires, pro refreshes session
    Refreshing --> Active: valid cookie → new token pair issued
    Refreshing --> LoggedOut: token expired or superseded
    Active --> LoggedOut: pro logs out
    Blocked --> [*]

Steps

1. Login (Actor: pro)

Pro sends POST /pro/auth/login with email (normalised to lowercase) and password. Rate limited to 10 requests per minute.

The system looks up the Pro by email, verifies the password hash, then checks isActive. If the account is deactivated, returns 403 immediately.

Any existing ProRefreshToken for the same deviceType (parsed from User-Agent header: "web" or "mobile") is deleted before issuing a new one. This enforces a maximum of one active session per device type.

Triggers: Pro submits login form Outcome: Access token in response body; refresh token as httpOnly cookie proRefreshToken (7-day maxAge)

2. Authenticated Requests (Actor: pro)

The client sends Authorization: Bearer <accessToken> on every API request. The authenticatePro hook verifies the JWT using PRO_JWT_SECRET, then performs a DB lookup on the Pro to check isActive and enforce the CNI gate (see cni-upload-analysis).

Triggers: Any protected route call Outcome: request.proId set; request proceeds or 401/403 returned

3. Token Refresh (Actor: system)

When the 15-min access token expires, the client sends POST /pro/auth/refresh. The server reads the proRefreshToken cookie, hashes it with SHA-256, and looks up the ProRefreshToken row.

  • If not found → 401 SESSION_EXPIRED / SESSION_SUPERSEDED
  • If found but expired → 401 SESSION_EXPIRED / TOKEN_EXPIRED
  • If the Pro is inactive → deletes the token, returns 401

On success: the old ProRefreshToken row is deleted and a new one is created (rotation). A new access token and a new refresh cookie are returned.

Triggers: Client detects expired access token and retries Outcome: New token pair; old refresh token invalidated

4. Logout (Actor: pro)

POST /pro/auth/logout. The server reads the cookie, hashes it, and deletes matching ProRefreshToken rows. The cookie is cleared by setting maxAge: 0. No auth required — the endpoint is always accessible (cookie may already be absent).

Triggers: Pro clicks “Se déconnecter” Outcome: Session terminated; cookie cleared

Error States

  • Invalid credentials → 401 UNAUTHORIZED
  • Deactivated account (login) → 403 ACCOUNT_DISABLED — “Votre compte a été désactivé. Contactez support@sealion.fr
  • Deactivated account (refresh) → 401 UNAUTHORIZED — “Account unavailable”; refresh token deleted
  • No refresh cookie → 401 UNAUTHORIZED — “No refresh token”
  • Token not found in DB → 401 SESSION_EXPIRED / SESSION_SUPERSEDED
  • Token expired → 401 SESSION_EXPIRED / TOKEN_EXPIRED