Files
fm_be/docs/frontend-sso-exchange-flow.md
2026-07-16 22:16:45 +07:00

3.8 KiB

Frontend Flow: Microsoft ssoSilent + Backend /auth/exchange

This document describes the recommended frontend authentication flow when using:

  • Microsoft Entra ssoSilent on the frontend
  • Backend session tokens (access + refresh) issued by /api/v1/auth/exchange

Goal

Use Microsoft silent SSO to re-establish identity, then exchange that identity for backend session cookies/tokens used by API requests.

Prerequisites

  • Frontend already integrated with MSAL and can call ssoSilent.
  • Backend has POST /api/v1/auth/exchange.
  • User account is already linked/recognized by backend SSO mapping.
  • Backend uses cookie-based auth session (access + refresh cookies).

High-Level Sequence

  1. Frontend attempts to call backend API with current backend access session.
  2. If backend responds 401, frontend calls POST /api/v1/auth/refresh once.
  3. If refresh fails (401), frontend tries Microsoft ssoSilent.
  4. If ssoSilent succeeds, frontend calls POST /api/v1/auth/exchange.
  5. Backend issues fresh auth cookies. Frontend retries the original API request.
  6. If ssoSilent fails with interaction_required, frontend logs out local app session and redirects user to interactive login.

Detailed Frontend Logic

1) App startup / route guard

  1. Try calling a lightweight authenticated endpoint (for example /api/v1/auth/me).
  2. If response is 200, continue app normally.
  3. If response is 401, run the recovery flow below.

2) Recovery flow for backend 401

  1. Call POST /api/v1/auth/refresh once.
  2. If refresh is successful (200), retry the original request and continue.
  3. If refresh fails (401), call Microsoft ssoSilent.

3) On successful ssoSilent

The frontend receives a Microsoft auth result (example fields):

  • accessToken
  • idToken
  • idTokenClaims

Send them to backend exchange endpoint:

POST /api/v1/auth/exchange
Content-Type: application/json
{
  "data": {
    "type": "auth_exchange",
    "attributes": {
      "access_token": "<ms_access_token>",
      "id_token": "<ms_id_token>",
      "id_token_claims": {
        "sub": "...",
        "preferred_username": "user@company.com",
        "name": "User Name",
        "tid": "...",
        "oid": "...",
        "exp": 1715603600
      }
    }
  }
}

Expected behavior:

  • 200: backend sets fresh session cookies -> frontend retries previous API call.
  • 401: identity not linked / invalid token -> treat as not authenticated and move to login.

4) On failed ssoSilent

If frontend receives:

  • errorCode = "interaction_required" (or equivalent no-session/no-cookie condition)

Then:

  1. Clear local app auth state (user store, cached flags, pending retries).
  2. Redirect to interactive login page/flow.

Do not loop ssoSilent repeatedly.

Retry Rules (Important)

  • Only attempt each step once per failure chain:
    • one /auth/refresh
    • one ssoSilent
    • one /auth/exchange
  • Use a request queue/lock to prevent multiple parallel refresh/exchange attempts.
  • If exchange fails, stop retries and force user to login interactively.

Suggested Error Handling Matrix

  • Backend API 401:
    • action: /auth/refresh
  • /auth/refresh 401:
    • action: ssoSilent
  • ssoSilent success:
    • action: /auth/exchange
  • ssoSilent interaction_required:
    • action: clear session + redirect login
  • /auth/exchange 401:
    • action: clear session + redirect login

Security Notes

  • Do not store backend refresh tokens in JavaScript storage.
  • Prefer HttpOnly, Secure, SameSite cookies from backend.
  • Avoid logging raw Microsoft tokens in browser console or monitoring payloads.

Optional UX Improvement

When redirecting to login after interaction_required, preserve the intended route:

  • Save current path (returnTo) and restore it after successful login.