ci: docker_tag=ext chore(root): sync submodules, logging, ignores, backups
This commit is contained in:
parent
851d900dcb
commit
faa21568ef
12
.gitignore
vendored
12
.gitignore
vendored
@ -5,6 +5,14 @@
|
|||||||
*.backup/
|
*.backup/
|
||||||
backup/
|
backup/
|
||||||
|
|
||||||
|
.cargo/
|
||||||
|
Cargo.lock
|
||||||
|
*/.cargo/
|
||||||
|
*/Cargo.lock
|
||||||
|
|
||||||
|
*.cargo/
|
||||||
|
*Cargo.lock
|
||||||
|
|
||||||
# Fichiers temporaires
|
# Fichiers temporaires
|
||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
@ -15,7 +23,6 @@ backup/
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!env.master
|
!env.master
|
||||||
!env.example
|
|
||||||
|
|
||||||
# Données et logs
|
# Données et logs
|
||||||
data/
|
data/
|
||||||
@ -96,5 +103,4 @@ supervisor-logs/
|
|||||||
# Scripts de déploiement temporaires
|
# Scripts de déploiement temporaires
|
||||||
deploy-*.tmp
|
deploy-*.tmp
|
||||||
setup-*.tmp
|
setup-*.tmp
|
||||||
|
.cursor-server
|
||||||
lecoffre_node/.env.master.bak_20250922162546
|
|
||||||
|
40
IA_agents/deploy_front_ext.md
Normal file
40
IA_agents/deploy_front_ext.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
## Déploiement maîtrisé du frontend (lecoffre-front) – flux ext
|
||||||
|
|
||||||
|
Objectif: construire l’image via la CI (tag `ext`) avec les variables build-time, exécuter le conteneur avec les variables runtime de `lecoffre_node/.env.master`, puis valider l’environnement et CORS/state.
|
||||||
|
|
||||||
|
### Pré-requis
|
||||||
|
- Variables `NEXT_PUBLIC_*` et autres correctement définies dans `lecoffre_node/.env.master`.
|
||||||
|
- CI configurée pour builder `git.4nkweb.com/4nk/lecoffre-front:ext` quand on pousse la branche et le tag `ext`.
|
||||||
|
|
||||||
|
### Déclenchement CI (manuel)
|
||||||
|
1. Commit sur la branche `ext` avec le préfixe: `ci: docker_tag=ext`.
|
||||||
|
2. Push explicite de la branche et du tag:
|
||||||
|
- `git push origin refs/heads/ext:refs/heads/ext`
|
||||||
|
- `git tag -f ext && git push origin refs/tags/ext:refs/tags/ext -f`
|
||||||
|
|
||||||
|
### Script de déploiement et validation
|
||||||
|
Le script `scripts/deploy_front_ext.sh` réalise:
|
||||||
|
- Pull de l’image et redémarrage du service (optionnel)
|
||||||
|
- Vérification que les `NEXT_PUBLIC_*` du runtime (`/api/env`) correspondent aux valeurs de `.env.master`
|
||||||
|
- Vérification CORS sur dev3 (OPTIONS 204) et POST `/api/v1/idnot/state` (200 + `state`)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
```
|
||||||
|
scripts/deploy_front_ext.sh [--ci] [--no-pull] [--no-up] [--validate-only]
|
||||||
|
```
|
||||||
|
- `--ci`: affiche un rappel pour déclencher la CI (aucun git dans le script)
|
||||||
|
- `--no-pull`: ne fait pas le `docker compose pull`
|
||||||
|
- `--no-up`: ne redémarre pas le service
|
||||||
|
- `--validate-only`: uniquement les validations (pas de pull ni up)
|
||||||
|
|
||||||
|
### Points de contrôle
|
||||||
|
- `docker-compose.yml` utilise `image: git.4nkweb.com/4nk/lecoffre-front:ext` (pas de build local)
|
||||||
|
- Les `NEXT_PUBLIC_*` sont cohérents entre le build (CI) et le runtime (`.env.master`)
|
||||||
|
- CORS dev3 renvoie `Access-Control-Allow-Origin: https://dev4.4nkweb.com` sur OPTIONS et POST
|
||||||
|
- L’endpoint `state` renvoie 200 et un champ `state`
|
||||||
|
|
||||||
|
### En cas d’erreur
|
||||||
|
- Variable manquante/mismatch: corriger `lecoffre_node/.env.master` puis relancer le script
|
||||||
|
- CORS en erreur: vérifier Nginx dev3 (headers, proxy_hide_header, logique d’Origin)
|
||||||
|
|
||||||
|
|
42
IA_agents/env-centralisation-policy.md
Normal file
42
IA_agents/env-centralisation-policy.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Politique de centralisation des variables et secrets (.env.master)
|
||||||
|
|
||||||
|
Objectif: toutes les variables d’environnement (publiques et privées) utilisées par les services doivent être centralisées dans `lecoffre_node/.env.master`. Aucun fichier Dockerfile ni docker-compose ne doit redéclarer des valeurs en dur qui divergeraient.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
|
||||||
|
- Fichier source unique: `lecoffre_node/.env.master`
|
||||||
|
- Chargement docker-compose: tous les services référencent `env_file: .env.master` et n’overrident pas les clés déjà définies.
|
||||||
|
- Aucune valeur sensible dans les Dockerfile/images. Utiliser exclusivement les variables passées par l’environnement (build args pour le front, runtime env pour les autres).
|
||||||
|
- Variables publiques Next.js: préfixe `NEXT_PUBLIC_` (intégrées au build). Variables privées: sans préfixe (non exposées au client).
|
||||||
|
- Secrets: clés API, tokens, mots de passe restent dans `.env.master` (jamais commités hors de ce dépôt privé). Fournir un `ENV_EXAMPLE.md` synchronisé.
|
||||||
|
|
||||||
|
## Mapping par service
|
||||||
|
|
||||||
|
- lecoffre-front (Next.js)
|
||||||
|
- Build-time: `NEXT_PUBLIC_*` via docker-compose build args → Dockerfile ARG/ENV → Next publicRuntimeConfig/env
|
||||||
|
- Runtime: idem (les valeurs restent exportées pour l’inspection)
|
||||||
|
- sdk_signer
|
||||||
|
- Runtime uniquement: `SIGNER_*` (ex: `SIGNER_API_KEY`, `SIGNER_WS_URL`, etc.) chargées via `env_file` sans override local.
|
||||||
|
- sdk_relay, sdk_storage, ihm_client
|
||||||
|
- Runtime: variables `SDK_RELAY_*`, `VITE_*` (pour IHM), etc. via `.env.master`
|
||||||
|
- Autres services (bitcoin, blindbit, …)
|
||||||
|
- Utiliser l’env pour toute config personnalisable; ne pas coder en dur.
|
||||||
|
|
||||||
|
## Bonnes pratiques
|
||||||
|
|
||||||
|
- N’ajouter de nouvelles variables qu’en les documentant dans `ENV_EXAMPLE.md` et ce fichier.
|
||||||
|
- Préférer des URLs de services Docker (ex: `http://service:port`) au lieu d’IP/host externes.
|
||||||
|
- Interdire toute duplication: si une valeur existe dans `.env.master`, ne pas la re-déclarer ailleurs.
|
||||||
|
- Logique de fallback uniquement côté code applicatif (jamais dans l’infra).
|
||||||
|
|
||||||
|
## Vérification rapide
|
||||||
|
|
||||||
|
- Front: `GET /lecoffre/api/env` et `/lecoffre/env` (no-store) pour voir les `NEXT_PUBLIC_*` en vigueur.
|
||||||
|
- Container: `docker exec <svc> env | grep -E '^(PREFIX_)'` pour contrôler.
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
1) Ajouter/mettre à jour la variable dans `.env.master`
|
||||||
|
2) Mettre à jour `ENV_EXAMPLE.md`
|
||||||
|
3) Rebuild/restart si nécessaire (front: rebuild; autres: recreate)
|
||||||
|
4) Vérifier via endpoints/container
|
@ -1,127 +1,104 @@
|
|||||||
### IdNot – Flux agnostique du front (dev3 backend, dev4 frontend)
|
IdNot front-agnostic flow (durable setup)
|
||||||
|
|
||||||
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.
|
Overview
|
||||||
|
|
||||||
### Vue d’ensemble
|
- Goal: Backend is agnostic of the frontend, supports any domain and localhost. IdNot keeps the fixed redirect_uri, Nginx forwards to backend, backend returns to the real frontend with an authToken in the fragment.
|
||||||
- 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
|
Data flow
|
||||||
- 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)
|
1) Frontend (Next.js) requests state
|
||||||
Ajouter dans l’environnement du back:
|
- POST https://dev3.4nkweb.com/api/v1/idnot/state
|
||||||
```bash
|
- Body: { next_url: <absolute URL to /authorized-client of the current front> }
|
||||||
BACK_HMAC_SECRET=<random-long-hex>
|
- Returns: { state }
|
||||||
STATE_TTL_SECONDS=180
|
|
||||||
ALLOW_LOCALHOST_REDIRECTS=true
|
|
||||||
ALLOWED_REDIRECT_HOST_PATTERNS=^dev4\.4nkweb\.com$,^localhost$,^127\.0\.0\.1$
|
|
||||||
```
|
|
||||||
|
|
||||||
### Implémentation – Backend (dev3)
|
2) Frontend builds authorize URL and redirects
|
||||||
- Service de state `src/services/state.service.ts`
|
- authorize = ${NEXT_PUBLIC_IDNOT_BASE_URL}${NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT}
|
||||||
- `signState(nextUrl: string)` → `{state}`
|
- query: client_id, redirect_uri = NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED, scope=openid,profile, response_type=code, 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
|
3) IdNot redirects to the fixed redirect
|
||||||
- `POST /api/v1/idnot/state` (body: `{ next_url }`) → 200 `{ state }`
|
- https://lecoffreio.4nkweb.com/authorized-client?code=...&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
|
4) Nginx (dev4) 301 → dev3 backend callback
|
||||||
```ts
|
- https://dev3.4nkweb.com/idnot/callback?code=...&state=...
|
||||||
router.post('/api/v1/idnot/state', StateHandlers.createState)
|
|
||||||
router.get('/idnot/callback', IdNotCallbackHandlers.callback)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx – dev3
|
5) Backend callback
|
||||||
- Vhost capteur du redirect IdNot (ex: `/etc/nginx/sites-available/local.4nkweb.com-3000`)
|
- Verifies state (HMAC, TTL, nonce, host allowlist)
|
||||||
Avant:
|
- Exchanges code with IdNot
|
||||||
```nginx
|
- Creates session/authToken
|
||||||
return 301 https://dev4.4nkweb.com/lecoffre$request_uri;
|
- 302 → next_url#authToken=...
|
||||||
```
|
|
||||||
Après:
|
|
||||||
```nginx
|
|
||||||
return 301 https://dev3.4nkweb.com/idnot/callback$request_uri;
|
|
||||||
```
|
|
||||||
|
|
||||||
- Vhost principal dev3 (ex: `/etc/nginx/sites-available/dev3.4nkweb.com`)
|
Frontend requirements
|
||||||
```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)
|
- Expose runtime variables in Next.js (NEXT_PUBLIC_*):
|
||||||
Ajouter au build/environnement front:
|
- NEXT_PUBLIC_IDNOT_BASE_URL=https://qual-connexion.idnot.fr
|
||||||
```bash
|
- NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT=/IdPOAuth2/authorize/ (or provider-specific)
|
||||||
NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED=http://local.4nkweb.com:3000/authorized-client
|
- NEXT_PUBLIC_IDNOT_CLIENT_ID=<from IdNot>
|
||||||
NEXT_PUBLIC_BACK_BASE=https://dev3.4nkweb.com
|
- NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED=https://lecoffreio.4nkweb.com/authorized-client
|
||||||
|
- NEXT_PUBLIC_FRONT_APP_HOST=https://dev4.4nkweb.com/lecoffre
|
||||||
|
- NEXT_PUBLIC_BACK_BASE=https://dev3.4nkweb.com
|
||||||
|
|
||||||
# Rappel API front (déjà en place)
|
- Login button flow (simplified):
|
||||||
NEXT_PUBLIC_BACK_API_PROTOCOL=https
|
1) POST ${NEXT_PUBLIC_BACK_BASE}/api/v1/idnot/state { next_url: window.location.origin + '/authorized-client' }
|
||||||
NEXT_PUBLIC_BACK_API_HOST=dev4.4nkweb.com
|
2) On {state}, redirect to authorize with &state=
|
||||||
NEXT_PUBLIC_BACK_API_PORT=443
|
|
||||||
NEXT_PUBLIC_BACK_API_ROOT_URL=/api
|
|
||||||
NEXT_PUBLIC_BACK_API_VERSION=v1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Implémentation – Front (dev4)
|
- Callback page
|
||||||
- Exposition des variables (next.config.js)
|
- Read authToken from window.location.hash
|
||||||
- `NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED`, `NEXT_PUBLIC_BACK_BASE`
|
- Store token then clear hash
|
||||||
- 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é
|
Backend requirements (summary)
|
||||||
- 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)
|
- Env:
|
||||||
1) Front → Back: POST `/api/v1/idnot/state` body `{ next_url }`
|
- BACK_HMAC_SECRET=<random-long-hex>
|
||||||
2) Back → Front: `{ state }`
|
- STATE_TTL_SECONDS=180
|
||||||
3) Front → IdNot: `/authorize?client_id=...&redirect_uri=http://local.4nkweb.com:3000/authorized-client&state=...`
|
- ALLOW_LOCALHOST_REDIRECTS=true
|
||||||
4) IdNot → dev3 Nginx: `http://local.4nkweb.com:3000/authorized-client?code&state`
|
- ALLOWED_REDIRECT_HOST_PATTERNS=^dev4\.4nkweb\.com$,^localhost$,^127\.0\.0\.1$
|
||||||
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
|
- Endpoints:
|
||||||
- `POST https://dev3.4nkweb.com/api/v1/idnot/state` → 200, `{ state }`
|
- POST /api/v1/idnot/state → returns signed state with {next_url, nonce, ts}
|
||||||
- `GET https://dev3.4nkweb.com/idnot/callback?code=<réel>&state=<state>` → 302 vers `next_url#authToken=...`
|
- GET /idnot/callback → verifies state, exchanges code, then 302 to next_url#authToken=...
|
||||||
- Front: bouton IdNot → parcours complet → session active
|
|
||||||
|
|
||||||
### Points d’attention
|
Curl note (important)
|
||||||
- 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
|
|
||||||
|
|
||||||
|
- When testing with curl, ensure a valid JSON body is actually sent. Some shells/options can result in an empty or malformed body, causing HTTP 500.
|
||||||
|
- Use Content-Type: application/json and --data-binary, for example:
|
||||||
|
curl -i -m 8 -X POST "https://dev3.4nkweb.com/api/v1/idnot/state" \
|
||||||
|
-H "Origin: https://lecoffreio.4nkweb.com" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary '{"next_url":"https://lecoffreio.4nkweb.com/authorized-client"}'
|
||||||
|
|
||||||
|
Nginx notes
|
||||||
|
|
||||||
|
- On dev4 (front): host lecoffreio.4nkweb.com must return 301 to dev3 callback, preserving full query string.
|
||||||
|
- On dev3 (back): proxy /idnot/callback and /api/ to the backend server.
|
||||||
|
|
||||||
|
Docker Compose (durable)
|
||||||
|
|
||||||
|
- Build lecoffre-front locally to ensure latest code and env are used:
|
||||||
|
lecoffre-front:
|
||||||
|
build:
|
||||||
|
context: ../lecoffre-front
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file:
|
||||||
|
- .env.master
|
||||||
|
|
||||||
|
- Keep .env.master at lecoffre_node/.env.master and include the NEXT_PUBLIC_* variables above. See .env.master.example.
|
||||||
|
|
||||||
|
Testing checklist
|
||||||
|
|
||||||
|
- On login:
|
||||||
|
- Network shows POST /api/v1/idnot/state before redirect
|
||||||
|
- IdNot authorize URL contains &state=
|
||||||
|
- dev3 /idnot/callback returns 302 to next_url#authToken=...
|
||||||
|
- Front stores token and proceeds
|
||||||
|
|
||||||
|
Security considerations
|
||||||
|
|
||||||
|
- HMAC-signed state with TTL and nonce (anti-replay)
|
||||||
|
- Strict host allowlist for next_url and localhost allowed only if ALLOW_LOCALHOST_REDIRECTS=true
|
||||||
|
- Prefer fragment (#authToken) to avoid logging tokens in proxies
|
||||||
|
|
||||||
|
Operations
|
||||||
|
|
||||||
|
- To rebuild and restart only the frontend after changes:
|
||||||
|
docker compose build lecoffre-front
|
||||||
|
docker compose up -d --no-deps --force-recreate lecoffre-front
|
||||||
|
63
IA_agents/logging_idnot.md
Normal file
63
IA_agents/logging_idnot.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Journalisation et analyse du flux IdNot
|
||||||
|
|
||||||
|
## Nginx (serveur web du front)
|
||||||
|
|
||||||
|
- Fichier d’inclusion: `/home/debian/4NK_env/lecoffre_node/conf/nginx/logging.conf`
|
||||||
|
- Inclus dans le vhost: `/home/debian/4NK_env/lecoffre_node/conf/nginx/dev4.4nkweb.com-https.conf`
|
||||||
|
- Logs générés:
|
||||||
|
- Access: `/var/log/nginx/lecoffre_front_access.log` (format JSON `lecoffre_json`)
|
||||||
|
- Error: `/var/log/nginx/lecoffre_front_error.log`
|
||||||
|
|
||||||
|
Champs JSON importants: `time`, `request_id`, `method`, `uri`, `status`, `request_time`, `upstream_status`, `upstream_response_time`, `x_forwarded_for`.
|
||||||
|
|
||||||
|
Propagation d’identifiant de requête:
|
||||||
|
- `X-Request-ID` client → mappé dans `$x_request_id` (fallback `$request_id`).
|
||||||
|
- Re-transmis aux upstream via `proxy_set_header X-Request-ID $x_request_id;`.
|
||||||
|
|
||||||
|
Commandes utiles:
|
||||||
|
```bash
|
||||||
|
# Dernières lignes des requêtes IdNot state
|
||||||
|
grep '"uri":"/api/v1/idnot/state' /var/log/nginx/lecoffre_front_access.log | tail -n 50 | jq .
|
||||||
|
|
||||||
|
# Requêtes IdNot auth
|
||||||
|
grep '"uri":"/api/v1/idnot/auth' /var/log/nginx/lecoffre_front_access.log | tail -n 50 | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Front (lecoffre-front)
|
||||||
|
|
||||||
|
- Ajout d’un `X-Request-ID` généré côté front lors du `POST /api/v1/idnot/state` pour corrélation.
|
||||||
|
- Code: `src/front/Api/Auth/IdNot/index.ts` (entête `X-Request-ID: front_<rand>`)
|
||||||
|
|
||||||
|
## Backend (dev3)
|
||||||
|
|
||||||
|
- À surveiller dans les logs applicatifs:
|
||||||
|
- `POST /api/v1/idnot/state`: génération de state (HMAC/TTL/nonce)
|
||||||
|
- `POST /api/v1/idnot/auth`: échange du code, puis appels vers IdNot et API Annuaire V2
|
||||||
|
- Appels sortants vers `https://qual-api.notaires.fr/...`: status, headers, body (tronqué), erreurs de parsing JSON
|
||||||
|
|
||||||
|
Conseils d’instrumentation backend:
|
||||||
|
- Loguer `X-Request-ID`, `Authorization` masqué, `Content-Type`, `Accept`.
|
||||||
|
- En cas d’échec JSON, loguer les 200 premiers caractères du body et le header `content-type`.
|
||||||
|
|
||||||
|
## Points à surveiller spécifiquement
|
||||||
|
|
||||||
|
- `status` 4xx/5xx sur `/api/v1/idnot/auth` et temps `upstream_response_time` élevés.
|
||||||
|
- Réponses non-JSON de l’API Annuaire (ex: body commençant par "No context") → probable manque de contexte/jeton.
|
||||||
|
- Cohérence `state` (émis côté backend) et `redirect_uri` fixe.
|
||||||
|
|
||||||
|
## Vérifications rapides
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Génération de state (OK attendu)
|
||||||
|
curl -sS --connect-timeout 3 --max-time 8 -X POST \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"next_url":"https://dev4.4nkweb.com/lecoffre/authorized-client"}' \
|
||||||
|
https://dev4.4nkweb.com/api/v1/idnot/state | jq .
|
||||||
|
|
||||||
|
# Auth (nécessite code valide)
|
||||||
|
curl -sS --connect-timeout 3 --max-time 10 -X POST \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-H "X-Request-ID: debug_manual_$(date +%s)" \
|
||||||
|
-d '{"code":"<CODE_IDNOT>"}' \
|
||||||
|
https://dev4.4nkweb.com/api/v1/idnot/auth | jq .
|
||||||
|
```
|
@ -10,13 +10,16 @@ Tous les documents des projets doivent être dans un dossier `docs/`
|
|||||||
|
|
||||||
- Tres important : ne modifie jamais d'autres branches git que celles courrantes, sous aucun prétexte.
|
- Tres important : ne modifie jamais d'autres branches git que celles courrantes, sous aucun prétexte.
|
||||||
|
|
||||||
|
|
||||||
- Tres important : ne modifie jamais les fichiers de configuration mais demande à l'utilisateur de faire les modifications.
|
- Tres important : ne modifie jamais les fichiers de configuration mais demande à l'utilisateur de faire les modifications.
|
||||||
|
|
||||||
|
- Tres important : si tu peux explicitement et exceptionnellement modifier les fichiers de configuration pour par exemple éviter des leaks de vrais données sur le git ou autres cas de ce genre ajoute dans la data : "FAKE-DATA-IA-"
|
||||||
|
|
||||||
- Tres important : ne créé pas de versions de test ou temporaires.
|
- Tres important : ne créé pas de versions de test ou temporaires.
|
||||||
|
|
||||||
- Tres important : corrige toutes les erreurs même petites avant de passer à la suite.
|
- Tres important : corrige toutes les erreurs même petites avant de passer à la suite.
|
||||||
|
|
||||||
|
- Tres important : fiabiliser les prochains déploiements c'est plus important que d'avancer un déploiement.
|
||||||
|
|
||||||
- Tres important : corrige sans simplifier ,sans contourner, sans faire de proxy, sans versions supplémentaires
|
- Tres important : corrige sans simplifier ,sans contourner, sans faire de proxy, sans versions supplémentaires
|
||||||
|
|
||||||
- Tres important : corrige en améliorant les versions actuelless des fichiers.
|
- Tres important : corrige en améliorant les versions actuelless des fichiers.
|
||||||
@ -32,9 +35,8 @@ Tous les documents des projets doivent être dans un dossier `docs/`
|
|||||||
- Très important : priorise toujours des principe aux effets directs et simples à investiguer sans multipler les couches
|
- Très important : priorise toujours des principe aux effets directs et simples à investiguer sans multipler les couches
|
||||||
|
|
||||||
- Très important : On parle en français.
|
- Très important : On parle en français.
|
||||||
prochains déploiements c'est plus important que d'avancer dans la toto.
|
|
||||||
|
|
||||||
- Très important : Ne modifie pas les valeurs des conf, demande toujours avant.
|
- Très important : Ne modifie pas les valeurs des environnements et conf, demande toujours avant.
|
||||||
|
|
||||||
- Très important : Gere les variables d'environnement avec : `IA_agents/env-centralisation-policy.md`
|
- Très important : Gere les variables d'environnement avec : `IA_agents/env-centralisation-policy.md`
|
||||||
|
|
||||||
@ -50,6 +52,10 @@ prochains déploiements c'est plus important que d'avancer dans la toto.
|
|||||||
|
|
||||||
- Très important : avec ce que tu apprends créer ou met à jour au fil de l'eau les docs de `IA_agents\rex.md`
|
- Très important : avec ce que tu apprends créer ou met à jour au fil de l'eau les docs de `IA_agents\rex.md`
|
||||||
|
|
||||||
|
- Tu as curl pour faire les vérifs n'attend pas infinement le retour
|
||||||
|
|
||||||
|
- Tu as l'acces en écriture aux fiichiers de configuration ngnix
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Note** : Ce prompt est basé sur `IA_agents/prompts/prompt-global.md`.
|
**Note** : Ce prompt est basé sur `IA_agents/prompts/prompt-global.md`.
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
IdNot front-agnostic flow (durable setup)
|
|
||||||
|
|
||||||
Overview
|
|
||||||
|
|
||||||
- Goal: Backend is agnostic of the frontend, supports any domain and localhost. IdNot keeps the fixed redirect_uri, Nginx forwards to backend, backend returns to the real frontend with an authToken in the fragment.
|
|
||||||
|
|
||||||
Data flow
|
|
||||||
|
|
||||||
1) Frontend (Next.js) requests state
|
|
||||||
- POST https://dev3.4nkweb.com/api/v1/idnot/state
|
|
||||||
- Body: { next_url: <absolute URL to /authorized-client of the current front> }
|
|
||||||
- Returns: { state }
|
|
||||||
|
|
||||||
2) Frontend builds authorize URL and redirects
|
|
||||||
- authorize = ${NEXT_PUBLIC_IDNOT_BASE_URL}${NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT}
|
|
||||||
- query: client_id, redirect_uri = NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED, scope=openid,profile, response_type=code, state
|
|
||||||
|
|
||||||
3) IdNot redirects to the fixed redirect
|
|
||||||
- http://local.4nkweb.com:3000/authorized-client?code=...&state=...
|
|
||||||
|
|
||||||
4) Nginx (dev4) 301 → dev3 backend callback
|
|
||||||
- https://dev3.4nkweb.com/idnot/callback?code=...&state=...
|
|
||||||
|
|
||||||
5) Backend callback
|
|
||||||
- Verifies state (HMAC, TTL, nonce, host allowlist)
|
|
||||||
- Exchanges code with IdNot
|
|
||||||
- Creates session/authToken
|
|
||||||
- 302 → next_url#authToken=...
|
|
||||||
|
|
||||||
Frontend requirements
|
|
||||||
|
|
||||||
- Expose runtime variables in Next.js (NEXT_PUBLIC_*):
|
|
||||||
- NEXT_PUBLIC_IDNOT_BASE_URL=https://qual-connexion.idnot.fr
|
|
||||||
- NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT=/IdPOAuth2/authorize/ (or provider-specific)
|
|
||||||
- NEXT_PUBLIC_IDNOT_CLIENT_ID=<from IdNot>
|
|
||||||
- NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED=http://local.4nkweb.com:3000/authorized-client
|
|
||||||
- NEXT_PUBLIC_FRONT_APP_HOST=https://dev4.4nkweb.com/lecoffre
|
|
||||||
- NEXT_PUBLIC_BACK_BASE=https://dev3.4nkweb.com
|
|
||||||
|
|
||||||
- Login button flow (simplified):
|
|
||||||
1) POST ${NEXT_PUBLIC_BACK_BASE}/api/v1/idnot/state { next_url: window.location.origin + '/authorized-client' }
|
|
||||||
2) On {state}, redirect to authorize with &state=
|
|
||||||
|
|
||||||
- Callback page
|
|
||||||
- Read authToken from window.location.hash
|
|
||||||
- Store token then clear hash
|
|
||||||
|
|
||||||
Backend requirements (summary)
|
|
||||||
|
|
||||||
- Env:
|
|
||||||
- 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$
|
|
||||||
|
|
||||||
- Endpoints:
|
|
||||||
- POST /api/v1/idnot/state → returns signed state with {next_url, nonce, ts}
|
|
||||||
- GET /idnot/callback → verifies state, exchanges code, then 302 to next_url#authToken=...
|
|
||||||
|
|
||||||
Nginx notes
|
|
||||||
|
|
||||||
- On dev4 (front): host local.4nkweb.com:3000 must return 301 to dev3 callback, preserving full query string.
|
|
||||||
- On dev3 (back): proxy /idnot/callback and /api/ to the backend server.
|
|
||||||
|
|
||||||
Docker Compose (durable)
|
|
||||||
|
|
||||||
- Build lecoffre-front locally to ensure latest code and env are used:
|
|
||||||
lecoffre-front:
|
|
||||||
build:
|
|
||||||
context: ../lecoffre-front
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
env_file:
|
|
||||||
- .env.master
|
|
||||||
|
|
||||||
- Keep .env.master at lecoffre_node/.env.master and include the NEXT_PUBLIC_* variables above. See .env.master.example.
|
|
||||||
|
|
||||||
Testing checklist
|
|
||||||
|
|
||||||
- On login:
|
|
||||||
- Network shows POST /api/v1/idnot/state before redirect
|
|
||||||
- IdNot authorize URL contains &state=
|
|
||||||
- dev3 /idnot/callback returns 302 to next_url#authToken=...
|
|
||||||
- Front stores token and proceeds
|
|
||||||
|
|
||||||
Security considerations
|
|
||||||
|
|
||||||
- HMAC-signed state with TTL and nonce (anti-replay)
|
|
||||||
- Strict host allowlist for next_url and localhost allowed only if ALLOW_LOCALHOST_REDIRECTS=true
|
|
||||||
- Prefer fragment (#authToken) to avoid logging tokens in proxies
|
|
||||||
|
|
||||||
Operations
|
|
||||||
|
|
||||||
- To rebuild and restart only the frontend after changes:
|
|
||||||
docker compose build lecoffre-front
|
|
||||||
docker compose up -d --no-deps --force-recreate lecoffre-front
|
|
||||||
|
|
@ -1 +1 @@
|
|||||||
Subproject commit 310428dc42864c96dfc85767fbd857b2f3673fab
|
Subproject commit 33853c7a6af1bd4a59d3cf4e70057859e1df5406
|
@ -1 +1 @@
|
|||||||
Subproject commit c2d64fce15ed0741a2153af0ac52915b8e797543
|
Subproject commit 9aa6e8582534c47c4a1920e74331b7615eba0cd1
|
@ -1 +1 @@
|
|||||||
Subproject commit 0517aaaee543396bfac3d49aed30aade86021ea7
|
Subproject commit 30eb0380791e3a8c5635bef8461342008d1f3770
|
143
scripts/deploy_front_ext.sh
Executable file
143
scripts/deploy_front_ext.sh
Executable file
@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Deploy lecoffre-front (ext) via CI image and validate environment & CORS/state
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/deploy_front_ext.sh [--ci] [--no-pull] [--no-up] [--validate-only]
|
||||||
|
#
|
||||||
|
# Behavior:
|
||||||
|
# - By default pulls CI image and restarts only the frontend service, then validates:
|
||||||
|
# 1) NEXT_PUBLIC_* variables in running app match lecoffre_node/.env.master
|
||||||
|
# 2) CORS preflight on dev3 OK (204) with Access-Control-Allow-Origin for dev4
|
||||||
|
# 3) POST /api/v1/idnot/state on dev3 OK (200) and returns a state
|
||||||
|
# - --ci: print a reminder to push branch/tag ext to trigger CI (no git ops here for safety)
|
||||||
|
# - --no-pull: skip docker compose pull
|
||||||
|
# - --no-up: skip docker compose up (restart)
|
||||||
|
# - --validate-only: skip pull & up, run validations only
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
NODE_DIR="$ROOT_DIR/lecoffre_node"
|
||||||
|
|
||||||
|
DO_PULL=1
|
||||||
|
DO_UP=1
|
||||||
|
DO_CI_HINT=0
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--ci) DO_CI_HINT=1 ;;
|
||||||
|
--no-pull) DO_PULL=0 ;;
|
||||||
|
--no-up) DO_UP=0 ;;
|
||||||
|
--validate-only) DO_PULL=0; DO_UP=0 ;;
|
||||||
|
*) echo "Unknown arg: $arg" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $DO_CI_HINT -eq 1 ]]; then
|
||||||
|
echo "[HINT] Trigger CI by pushing branch 'ext' and tag 'ext' with commit message prefix 'ci: docker_tag=ext' (no git ops run by this script)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$NODE_DIR/.env.master" ]]; then
|
||||||
|
echo "[ERROR] Missing $NODE_DIR/.env.master" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pull and up only the frontend service
|
||||||
|
if [[ $DO_PULL -eq 1 ]]; then
|
||||||
|
echo "[STEP] docker compose pull lecoffre-front"
|
||||||
|
(cd "$NODE_DIR" && docker compose pull lecoffre-front)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $DO_UP -eq 1 ]]; then
|
||||||
|
echo "[STEP] docker compose up -d --no-deps lecoffre-front"
|
||||||
|
(cd "$NODE_DIR" && docker compose up -d --no-deps lecoffre-front)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for front to respond
|
||||||
|
FRONT_URL="http://localhost:3004"
|
||||||
|
echo "[STEP] Waiting for $FRONT_URL to be ready..."
|
||||||
|
ATTEMPTS=40
|
||||||
|
until curl -fsS "$FRONT_URL/" >/dev/null 2>&1; do
|
||||||
|
ATTEMPTS=$((ATTEMPTS-1)) || true
|
||||||
|
if [[ $ATTEMPTS -le 0 ]]; then
|
||||||
|
echo "[ERROR] Frontend not responding on $FRONT_URL" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "[OK] Frontend is responding"
|
||||||
|
|
||||||
|
# 0) Public URL sanity check (dev4) to catch 404/misrouting early
|
||||||
|
echo "[STEP] Public URL check https://dev4.4nkweb.com/lecoffre (expect 301->/lecoffre/)"
|
||||||
|
PUB_RESP=$(curl -siS "https://dev4.4nkweb.com/lecoffre?nocache=$(date +%s)" 2>/dev/null | sed -n '1,20p')
|
||||||
|
echo "${PUB_RESP}"
|
||||||
|
echo "${PUB_RESP}" | grep -qE "^HTTP/(1.1|2) 301" || { echo "[FAIL] Public URL /lecoffre not 301" >&2; exit 1; }
|
||||||
|
echo "${PUB_RESP}" | grep -qi "^location: .*?/lecoffre/" || { echo "[FAIL] /lecoffre does not redirect to /lecoffre/" >&2; exit 1; }
|
||||||
|
echo "[OK] /lecoffre redirects to /lecoffre/"
|
||||||
|
|
||||||
|
echo "[STEP] Public URL check https://dev4.4nkweb.com/lecoffre/ (expect 200)"
|
||||||
|
PUB_RESP_SLASH=$(curl -siS "https://dev4.4nkweb.com/lecoffre/?nocache=$(date +%s)" 2>/dev/null | sed -n '1,20p')
|
||||||
|
echo "${PUB_RESP_SLASH}"
|
||||||
|
echo "${PUB_RESP_SLASH}" | grep -qE "^HTTP/(1.1|2) 200" || { echo "[FAIL] Public URL /lecoffre/ not 200" >&2; exit 1; }
|
||||||
|
echo "[OK] /lecoffre/ returns 200"
|
||||||
|
|
||||||
|
# 1) Validate NEXT_PUBLIC_* variables against .env.master (container env is source of truth)
|
||||||
|
echo "[STEP] Validating NEXT_PUBLIC_* variables vs .env.master (container env)"
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "[ERROR] jq is required for validation" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract NEXT_PUBLIC_* from .env.master (ignore comments/blank)
|
||||||
|
mapfile -t FILE_KVS < <(grep -E '^[[:space:]]*NEXT_PUBLIC_[A-Za-z0-9_]+=' "$NODE_DIR/.env.master" | sed 's/^[[:space:]]*//' | sort)
|
||||||
|
|
||||||
|
MISMATCH=0
|
||||||
|
for kv in "${FILE_KVS[@]}"; do
|
||||||
|
key="${kv%%=*}"
|
||||||
|
expect="${kv#*=}"
|
||||||
|
# Normalize line endings and trim surrounding quotes if any
|
||||||
|
expect="$(printf '%s' "$expect" | tr -d '\r' | sed 's/^\"//; s/\"$//')"
|
||||||
|
# Read from running env
|
||||||
|
# Read from container environment (single source of truth at runtime)
|
||||||
|
actual="$(cd "$NODE_DIR" && docker compose exec -T lecoffre-front /bin/sh -lc "env | grep -E '^${key}=' | head -n1 | sed 's/^${key}=//'" || true)"
|
||||||
|
if [[ "$actual" != "$expect" ]]; then
|
||||||
|
echo "[MISMATCH] $key: running='$actual' vs .env.master='$expect'"
|
||||||
|
MISMATCH=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $MISMATCH -ne 0 ]]; then
|
||||||
|
echo "[FAIL] NEXT_PUBLIC_* variables mismatch."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[OK] NEXT_PUBLIC_* variables match .env.master"
|
||||||
|
|
||||||
|
# 2) CORS preflight check on dev3
|
||||||
|
echo "[STEP] CORS preflight (OPTIONS) to dev3 idnot/state"
|
||||||
|
PRE="$(curl -i -X OPTIONS 'https://dev3.4nkweb.com/api/v1/idnot/state' \
|
||||||
|
-H 'Origin: https://dev4.4nkweb.com' \
|
||||||
|
-H 'Access-Control-Request-Method: POST' \
|
||||||
|
-H 'Access-Control-Request-Headers: content-type' 2>/dev/null | sed -n '1,20p')"
|
||||||
|
echo "$PRE"
|
||||||
|
echo "$PRE" | grep -q "^HTTP/1.1 204" || { echo "[FAIL] Preflight not 204" >&2; exit 1; }
|
||||||
|
echo "$PRE" | grep -qi "Access-Control-Allow-Origin: https://dev4.4nkweb.com" || { echo "[FAIL] A-C-A-Origin not dev4" >&2; exit 1; }
|
||||||
|
echo "[OK] Preflight CORS ok"
|
||||||
|
|
||||||
|
# 3) POST state on dev3
|
||||||
|
echo "[STEP] POST state to dev3"
|
||||||
|
STATE_RESP="$(curl -i -X POST 'https://dev3.4nkweb.com/api/v1/idnot/state' \
|
||||||
|
-H 'Origin: https://dev4.4nkweb.com' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
--data '{"next_url":"https://dev4.4nkweb.com/lecoffre/authorized-client"}' 2>/dev/null)"
|
||||||
|
echo "$STATE_RESP" | sed -n '1,30p'
|
||||||
|
echo "$STATE_RESP" | grep -q "^HTTP/1.1 200" || { echo "[FAIL] state endpoint not 200" >&2; exit 1; }
|
||||||
|
echo "$STATE_RESP" | grep -qi "Access-Control-Allow-Origin: https://dev4.4nkweb.com" || { echo "[FAIL] A-C-A-Origin not dev4 on POST" >&2; exit 1; }
|
||||||
|
STATE_JSON="$(echo "$STATE_RESP" | awk 'BEGIN{p=0} /^\r?$/{p=1;next} p{print}')"
|
||||||
|
STATE_VAL="$(echo "$STATE_JSON" | jq -r '.state // empty')"
|
||||||
|
if [[ -z "$STATE_VAL" ]]; then
|
||||||
|
echo "[FAIL] Missing state in response" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[OK] state received"
|
||||||
|
|
||||||
|
echo "[SUCCESS] Deployment validation completed."
|
@ -1 +1 @@
|
|||||||
Subproject commit 4a153ceefd6a9db31af04d829c0b87c319094e9f
|
Subproject commit 48704cb71bb1e96b7c0586133189d76449d5550f
|
@ -1 +1 @@
|
|||||||
Subproject commit 763c0e5a840e9ced2e6dbbda462f131b3227ae44
|
Subproject commit 089bf06502eef2fb49e99ba9e614ddf3cbf7c3a0
|
@ -1 +1 @@
|
|||||||
Subproject commit 682485abe4d3dc7ecfa9bc87317ccc3e817b1888
|
Subproject commit 50975a98e6ff581f560b15ec31396c8c560a15c9
|
Loading…
x
Reference in New Issue
Block a user