Skip to content

Story 32-5b : l'authentification par clé MCP (refs #723) - #752

Merged
guycorbaz merged 10 commits into
mainfrom
story/32-5b
Oct 25, 2026
Merged

guycorbaz merged 10 commits into
mainfrom
story/32-5b

Conversation

@guycorbaz

Copy link
Copy Markdown
Owner

Deuxième partie de la Story 32-5, après la 32-5a (#751) : la clé MCP devient un moyen d'authentification. Aucune route ne l'expose encore. Les routes de gestion viennent en 32-5c ; l'endpoint /mcp, en 32-6.

Ce qui est livré

  • Génération :
    • MCP_PREFIX = "kesh_mcp_" et generate_mcp_key ;
    • un cœur privé generate_prefixed, partagé avec le PAT et le jeton de réinitialisation, ce qui réduit OsRng.fill_bytes à une seule occurrence.
  • Portee devient un alias de kesh_db::entities::McpScope.
  • services::cles_mcp::authentifier(&Lecture, &Trace, jeton) :
    • le préfixe est contrôlé aux octets, avant tout accès à la base ;
    • la recherche passe par la Lecture, la dernière utilisation par la Trace ;
    • les refus — clé inconnue, révoquée ou expirée, société absente, créateur inactif, non administrateur ou d'une autre société, clé sans portée connue — rendent tous le même 401, et le motif ne va qu'au journal du serveur ;
    • une erreur de base se propage, elle n'est jamais convertie en 401.
  • La jointure de l'authentification exige en plus u.company_id = k.company_id.
  • Un jeton kesh_mcp_ présenté à /api/v1 rend 401, en en-tête comme en cookie, et un PAT n'authentifie pas une clé MCP.
  • Une clé restaurée authentifie. Une clé révoquée avant l'export revient révoquée ; une clé révoquée après l'export revient active, ce qui est l'état au moment de la sauvegarde.

Revue de code

Passe Modèle Bilan
P1 Sonnet ×3 B 0/0/0/3, E 0/0/1/5, A 0/0/0/3
P2 ciblée Opus 0/0/1/2
P3 ciblée Sonnet 0/0/0/2

Les deux LOW de la P3 sont tracés sur #738.

Mutations : 13 jouées (12 distinctes), toutes tuées.

Gates au dernier commit de code e64ef129

  • Gate complet : 4085/4085, 7 ignorés.
  • E2E complet : 315 passés et les 7 échecs KF-029 attendus.

main n'a pas bougé depuis la base de la branche (b723e3d9).

Liée à #723.

🤖 Generated with Claude Code

guycorbaz and others added 10 commits October 25, 2026 04:43
- auth/api_key.rs : MCP_PREFIX (kesh_mcp_), generate_mcp_key ; un cœur
  commun generate_prefixed pour le PAT, la clé MCP et le jeton de
  réinitialisation (un seul OsRng.fill_bytes).
- services/acces.rs : Portee devient l'alias de kesh_db::entities::McpScope
  (cinq portées, une seule énumération).
- services/cles_mcp.rs (neuf) : authentifier(&Lecture, &Trace, jeton) —
  préfixe exact avant tout accès à la base, recherche sur la Lecture,
  créateur actif et Administrateur (ROLE_CLE_MCP), dernière utilisation
  best-effort sur la Trace ; même 401 pour chaque refus, erreur de base
  propagée.
- repositories/mcp_keys.rs : un seul avertissement au chemin
  d'authentification (R5 de la revue de la 32-5a).
- Tests : cles_mcp_e2e.rs (neuf), clé restaurée qui authentifie
  (admin_full_import_e2e.rs), refus de portée par les quatre autres portées
  (services_motif.rs, services_rollout.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(refs #723)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (refs #723)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- E1 : identifiants de société et d'utilisateur distincts dans les montages
  (société leurre dans cles_mcp_e2e.rs, second administrateur créateur dans
  le test de la clé restaurée), assertés.
- B1 = E3 : find_active_auth_by_key_hash exige un créateur de la société de
  la clé (u.company_id = k.company_id), avec son test ; le test orphelin
  monte un créateur orphelin de la même société, pour que JOIN companies
  reste seul à le refuser.
- B3 = E4 : la clé révoquée revient révoquée, celle révoquée après l'export
  revient active (revoked_at relu après l'import).
- E6 : trois jetons hostiles portant le préfixe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n (refs #723)

Change Log, registre (C-32-5-43, C-32-5-44), reports au § Pour la 32-5c
(E5, B2, A2), annotation de la ligne de sprint-status (A1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- R1 : societe_absente_refusee monte un créateur orphelin de la même
  société absente, sur la même connexion, pour que seule la jointure à
  companies refuse la clé.
- R2 : les textes qui énumèrent les refus nomment le créateur d'une autre
  société (cles_mcp.rs, mcp_keys.rs).
- R3 : doc-comment de jetons_hostiles_avec_le_prefixe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n (refs #723)

Change Log, annotation de l'AC-b3 et de C-32-5-41.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant