128 lines
5.2 KiB
Markdown
128 lines
5.2 KiB
Markdown
### IdNot – Flux agnostique du front (dev3 backend, dev4 frontend)
|
||
|
||
Objectif: permettre à n'importe quel front (domaine inconnu, y compris localhost) d'utiliser l'auth IdNot sans modifier le redirect_uri déclaré chez IdNot.
|
||
|
||
### Vue d’ensemble
|
||
- Le redirect_uri IdNot reste: `http://local.4nkweb.com:3000/authorized-client`.
|
||
- Nginx sur dev3 intercepte ce host/port et redirige vers `https://dev3.4nkweb.com/idnot/callback` en conservant la query (`code`, `state`).
|
||
- Le front demande au back un `state` signé contenant `next_url` (URL finale dynamique du front).
|
||
- Le back valide `state`, échange le `code` chez IdNot, crée la session, puis 302 vers `next_url#authToken=<...>`.
|
||
|
||
### Composants
|
||
- Backend (dev3, lecoffre-back-mini)
|
||
- Frontend (dev4, lecoffre-front)
|
||
- Nginx dev3: capte `local.4nkweb.com:3000/authorized-client` → `/idnot/callback` et proxy /api
|
||
- Nginx dev4: sert `/lecoffre/` et proxifie `/api/` vers dev3 (inchangé pour ce flux)
|
||
|
||
### Variables d’environnement – Backend (dev3)
|
||
Ajouter dans l’environnement du back:
|
||
```bash
|
||
BACK_HMAC_SECRET=<random-long-hex>
|
||
STATE_TTL_SECONDS=180
|
||
ALLOW_LOCALHOST_REDIRECTS=true
|
||
ALLOWED_REDIRECT_HOST_PATTERNS=^dev4\.4nkweb\.com$,^localhost$,^127\.0\.0\.1$
|
||
```
|
||
|
||
### Implémentation – Backend (dev3)
|
||
- Service de state `src/services/state.service.ts`
|
||
- `signState(nextUrl: string)` → `{state}`
|
||
- `verifyState(state: string)` → `{ next_url, nonce, ts }`
|
||
- payload: `{ next_url, nonce, ts }` JSON
|
||
- signature: `HMAC-SHA256(payload, BACK_HMAC_SECRET)`
|
||
- TTL: `now - ts <= STATE_TTL_SECONDS`
|
||
- Anti-rejeu: `Map<nonce, expiry>` nettoyée périodiquement
|
||
- Validation `next_url`: URL absolue; schéma https, ou http autorisé seulement si `ALLOW_LOCALHOST_REDIRECTS=true` et host ∈ {`localhost`, `127.0.0.1`}; host match `ALLOWED_REDIRECT_HOST_PATTERNS`
|
||
|
||
- Handlers
|
||
- `POST /api/v1/idnot/state` (body: `{ next_url }`) → 200 `{ state }`
|
||
- `GET /idnot/callback` (query: `code`, `state`)
|
||
- `verifyState(state)` → récupère `next_url`
|
||
- `IdNotService.exchangeCodeForTokens(code)` (existant)
|
||
- Crée la session/authToken
|
||
- `302` vers `next_url#authToken=<...>` (fragment recommandé)
|
||
- Erreurs: 400 (state invalide), 502 (échec IdNot), 400 (next_url invalide)
|
||
|
||
- Routes à monter
|
||
```ts
|
||
router.post('/api/v1/idnot/state', StateHandlers.createState)
|
||
router.get('/idnot/callback', IdNotCallbackHandlers.callback)
|
||
```
|
||
|
||
### Nginx – dev3
|
||
- Vhost capteur du redirect IdNot (ex: `/etc/nginx/sites-available/local.4nkweb.com-3000`)
|
||
Avant:
|
||
```nginx
|
||
return 301 https://dev4.4nkweb.com/lecoffre$request_uri;
|
||
```
|
||
Après:
|
||
```nginx
|
||
return 301 https://dev3.4nkweb.com/idnot/callback$request_uri;
|
||
```
|
||
|
||
- Vhost principal dev3 (ex: `/etc/nginx/sites-available/dev3.4nkweb.com`)
|
||
```nginx
|
||
location ^~ /api/ { proxy_pass http://127.0.0.1:8080; include /etc/nginx/proxy_params; }
|
||
location = /idnot/callback { proxy_pass http://127.0.0.1:8080; include /etc/nginx/proxy_params; }
|
||
```
|
||
|
||
### Variables d’environnement – Front (dev4)
|
||
Ajouter au build/environnement front:
|
||
```bash
|
||
NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED=http://local.4nkweb.com:3000/authorized-client
|
||
NEXT_PUBLIC_BACK_BASE=https://dev3.4nkweb.com
|
||
|
||
# Rappel API front (déjà en place)
|
||
NEXT_PUBLIC_BACK_API_PROTOCOL=https
|
||
NEXT_PUBLIC_BACK_API_HOST=dev4.4nkweb.com
|
||
NEXT_PUBLIC_BACK_API_PORT=443
|
||
NEXT_PUBLIC_BACK_API_ROOT_URL=/api
|
||
NEXT_PUBLIC_BACK_API_VERSION=v1
|
||
```
|
||
|
||
### Implémentation – Front (dev4)
|
||
- Exposition des variables (next.config.js)
|
||
- `NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED`, `NEXT_PUBLIC_BACK_BASE`
|
||
- VariablesFront.ts
|
||
- Ajouter `IDNOT_REDIRECT_URI_FIXED?: string;`
|
||
- Ajouter `BACK_BASE?: string;`
|
||
- Démarrage IdNot (ex: `StepEmail/index.tsx`)
|
||
1) `POST ${BACK_BASE}/api/v1/idnot/state` avec `{ next_url: window.location.origin + '/authorized-client' }`
|
||
2) Récupérer `{ state }`
|
||
3) Construire l’URL IdNot avec:
|
||
- `redirect_uri=${NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED}`
|
||
- `state=${state}`
|
||
- `client_id`, `scope`, `response_type=code`
|
||
- Retour (page `authorized-client`)
|
||
- Lire `#authToken` dans le fragment
|
||
- Stocker cookie `leCoffreAccessToken`
|
||
- Nettoyer le hash
|
||
|
||
### Sécurité
|
||
- Pas d’open redirect: HMAC + TTL + nonce + whitelist host
|
||
- `#authToken` dans le fragment pour éviter logs/proxys
|
||
- Rate-limit sur `POST /api/v1/idnot/state`
|
||
- Logs: ne pas exposer `next_url` complet
|
||
|
||
### Séquence (texte)
|
||
1) Front → Back: POST `/api/v1/idnot/state` body `{ next_url }`
|
||
2) Back → Front: `{ state }`
|
||
3) Front → IdNot: `/authorize?client_id=...&redirect_uri=http://local.4nkweb.com:3000/authorized-client&state=...`
|
||
4) IdNot → dev3 Nginx: `http://local.4nkweb.com:3000/authorized-client?code&state`
|
||
5) Nginx dev3 → Back: `GET /idnot/callback?code&state`
|
||
6) Back → IdNot: échange de token; session
|
||
7) Back → Front: `302 next_url#authToken=...`
|
||
8) Front: stocke token, poursuit navigation
|
||
|
||
### Tests
|
||
- `POST https://dev3.4nkweb.com/api/v1/idnot/state` → 200, `{ state }`
|
||
- `GET https://dev3.4nkweb.com/idnot/callback?code=<réel>&state=<state>` → 302 vers `next_url#authToken=...`
|
||
- Front: bouton IdNot → parcours complet → session active
|
||
|
||
### Points d’attention
|
||
- Ajouter les domaines requis dans `ALLOWED_REDIRECT_HOST_PATTERNS`
|
||
- TTL court (180s) et purge des nonces
|
||
- En prod, préférer cookies sécurisés et durées minimales
|
||
|
||
|
||
|