0013-fediverse-auth-flow-routes.md 7222 octets

RFC 0013 — Fediverse: routes HTTP du flow d'auth AP-native (slice routes 2/N)

  • Auteur : EBO (rustwarden)
  • Date : 2026-05-10
  • Statut : Accepted (auto-revue mainteneur unique 2026-05-10, score 26/35)
  • Apps consommatrices concernées : toute app utilisant rustwarden-core qui veut permettre à un utilisateur d'un IdP fediverse externe de se connecter via HTTP/JSON.
  • Plan associé : docs/plans/2026-05-09-fediverse-identity-federation.md, Phase 2 — clôture HTTP.
  • Dépendances : RFC 0006 (FediverseAuthService), RFC 0011 (FediverseOutboundService), RFC 0012 (pattern routes + SignatureVerifier).

Summary

Exposer 5 routes HTTP qui rendent FediverseAuthService (RFC 0006) utilisable end-to-end :

  • POST /api/v1/auth/login/fediverse/initiate (côté client b.tld) — démarre un challenge.
  • GET /api/v1/auth/login/fediverse/status/{challenge_id} — polling.
  • POST /api/v1/auth/login/fediverse/consume/{challenge_id} — émet le JWT remote depuis un challenge Approved.
  • POST /fediverse/auth/inbox (côté IdP a.tld) — reçoit l'AuthRequest signé d'un autre Rustwarden.
  • POST /api/v1/fediverse/auth/consent (côté IdP) — Approve / Reject par l'utilisateur.

Le code reste minimal : chaque handler est un thin wrapper autour du service. Aucune UI n'est livrée (Phase 2 finale UI), aucune crypto réelle n'est ajoutée.

Motivation

Sans cette RFC, FediverseAuthService n'a aucun chemin HTTP : un consommateur souhaitant le flow doit écrire ses 5 handlers à la main. La promesse Phase 2 du master plan « 2 instances locales : bob@a.tld se connecte sur b.tld » est encore non-tenable.

Non-goals

  • Pas de UI HTML (pages /login/fediverse, /login/fediverse/waiting, /fediverse/consent) — rustwarden-ui Phase 2 finale.
  • Pas de polling SSE — les apps qui veulent un long-poll envelopperont status côté handler de leur app.
  • Pas de signature HTTP réelle — le verifier reste pluggable (SignatureVerifier de RFC 0010), AlwaysAcceptVerifier par défaut.
  • Pas de CSRF middleware sur les API JSON — les apps consommatrices configurent leur middleware (Axum-level) selon leur SPA.
  • Pas de routes team-invite — RFC 0014.

API proposée

Bodies JSON

// POST /api/v1/auth/login/fediverse/initiate
#[derive(Deserialize)] pub struct InitiateBody { pub handle: String, pub redirect_url: Option<String> }
#[derive(Serialize)]   pub struct InitiateResponse { pub challenge_id: Uuid, pub nonce: String, pub expires_at: String /* RFC3339 */ }

// GET /api/v1/auth/login/fediverse/status/{challenge_id}
#[derive(Serialize)] pub struct StatusResponse { pub challenge_id: Uuid, pub status: String /* pending|approved|rejected|expired|consumed */ }

// POST /api/v1/auth/login/fediverse/consume/{challenge_id}
#[derive(Serialize)] pub struct ConsumeResponse { pub jwt: String, pub remote_identity_id: Uuid, pub handle: String }

// POST /fediverse/auth/inbox  (IdP side)
//   body : raw AP `AuthRequest` JSON-LD (vérifié par SignatureVerifier).
//   Retour : 202 Accepted (challenge enregistré, l'utilisateur sera invité à
//   approuver via UI ou via l'API consent ci-dessous).

// POST /api/v1/fediverse/auth/consent
#[derive(Deserialize)] pub struct ConsentBody { pub challenge_id: Uuid, pub decision: String /* "approve"|"reject" */ }

Câblage

Le RustwardenBuilder::with_fediverse() mounte les 5 routes via une nouvelle fonction routes::auth_flow_router(...) chaînée après inbox. Les routes API JSON utilisent une AuthConfig partagée (la même qui sert au flow login local).

États HTTP

EndpointSuccèsErreurs
initiate201 Created400 handle invalide
status200 OK404 challenge inconnu
consume200 OK400 non Approved, 409 déjà Consumed
auth/inbox202 Accepted401 signature, 400 body
consent200 OK404 challenge inconnu, 409 déjà décidé

Analyse de compatibilité

  • Non-breaking : 5 nouveaux paths, gated par experimental-fediverse.
  • Pas de schéma DB.
  • Aucune API publique existante n'est modifiée.

Alternatives considérées

Alternative A (recommandée) — 5 endpoints REST minces

Pros : conforme aux conventions JSON existantes ; SPA-friendly ; reste dans l'esprit Phase 2.

Cons : 5 paths à mémoriser pour le consommateur.

Alternative B — Endpoint unique /api/v1/auth/login/fediverse avec champ action

Cons : nuit à la lisibilité de l'audit log + des middlewares de rate-limit.

Rejeté.

Alternative C — Tout en GraphQL

Cons : hors stack actuelle.

Rejeté.

Alternative D — Ne rien faire

Cons : Phase 2 reste théorique.

Rejeté.

Testing plan

10 tests :

  1. initiate_returns_201_with_challenge_id.
  2. initiate_returns_400_for_invalid_handle.
  3. status_returns_pending_for_fresh_challenge.
  4. status_returns_404_for_unknown_id.
  5. consume_returns_jwt_for_approved_challenge.
  6. consume_returns_400_when_pending.
  7. auth_inbox_returns_202_for_signed_authrequest.
  8. auth_inbox_returns_401_when_signature_missing.
  9. consent_approve_marks_challenge_approved.
  10. consent_reject_marks_challenge_rejected.

Tests via axum::Router + tower::ServiceExt::oneshot.

Migration guide

### Added (rustwarden-core, feature `experimental-fediverse`)

- 5 routes HTTP : `POST /api/v1/auth/login/fediverse/initiate`,
  `GET /api/v1/auth/login/fediverse/status/{id}`,
  `POST /api/v1/auth/login/fediverse/consume/{id}`,
  `POST /fediverse/auth/inbox`,
  `POST /api/v1/fediverse/auth/consent`.
- Module `rustwarden_core::fediverse::auth_routes`.
- Bodies / responses publics : `InitiateBody`, `InitiateResponse`,
  `StatusResponse`, `ConsumeResponse`, `ConsentBody`.

### Migration steps

1. Aucun changement obligatoire : les routes ne sont mountées que si
   `with_fediverse()` est appelé (déjà gated par feature).
2. Apps consommatrices SPA : peuvent maintenant piloter le flow
   complet via JSON.

Open questions

Toutes résolues à l'auto-revue :

  • TTL challenge : fediverse.auth_challenge_ttl_secs (300 s par défaut).
  • auth/inbox path : /fediverse/auth/inbox (distinct de /inbox qui sert les signaux lifecycle, scope clair).
  • Rate limiting : pas dans cette RFC ; les apps appliquent leur middleware comme pour /api/v1/auth/login.

Tracking issue

À ouvrir au moment de la PR.


Score d'évaluation

CritèreNote (1-5)Commentaire
Généralité4Endpoints standards REST, faciles à wrapper en SPA.
Stabilité API4Additif, gated.
Performance4Tous les handlers sont O(1) lookups.
Sécurité3Pas de rate-limit dans cette RFC, pas de CSRF (API JSON).
Complexité d'implémentation4~500 lignes (5 handlers + state + 10 tests).
Maintenance35 nouveaux paths à conserver compatibles.
Documentation4Rustdoc EN, doctest, CHANGELOG, migration guide.
Total26/35≥ 25 → Accepted