122 lines
3.8 KiB
Markdown
122 lines
3.8 KiB
Markdown
# 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:
|
|
|
|
```http
|
|
POST /api/v1/auth/exchange
|
|
Content-Type: application/json
|
|
```
|
|
|
|
```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.
|