0014-fediverse-invite-routes.md 6884 octets

RFC 0014 — Fediverse: routes HTTP des invitations cross-instance (slice routes 3/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 admin d'équipe d'inviter un utilisateur fediverse externe via HTTP/JSON, et à un IdP de gérer les invitations reçues.
  • Plan associé : docs/plans/2026-05-09-fediverse-identity-federation.md, Phase 3 — clôture HTTP.
  • Dépendances : RFC 0009 (FediverseInviteService), RFC 0012/0013 (pattern routes).

Summary

Exposer 6 routes HTTP qui rendent FediverseInviteService (RFC 0009) utilisable end-to-end :

Côté b.tld (admin de l'équipe) :

  • POST /api/v1/teams/{team_id}/members/invite-fediverse — démarre une invitation.
  • GET /api/v1/teams/{team_id}/invites — liste les invitations pending.
  • POST /api/v1/teams/{team_id}/invites/{invite_id}/revoke — révocation.

Côté a.tld (IdP, utilisateur invité) :

  • GET /api/v1/fediverse/invites — mes invitations en attente.
  • POST /api/v1/fediverse/invites/{invite_id}/accept — accepte.
  • POST /api/v1/fediverse/invites/{invite_id}/reject — refuse.

Chaque handler est un thin wrapper autour du service. Aucune UI livrée.

Motivation

Sans cette RFC, RFC 0009 reste sans chemin HTTP : un admin doit appeler le service Rust à la main pour inviter un user fediverse, et l'IdP n'a aucun moyen de présenter les invitations reçues. Le master plan §10 Phase 3 « b.tld invite alice@a.tld via UI → Alice voit l'invitation sur son IdP » devient enfin réalisable côté API JSON.

Non-goals

  • Pas de UI HTML (/admin/teams/{id} enrichie + /profile/invites) — Phase 3 finale UI.
  • Pas d'authentification dans cette RFC — les apps consommatrices appliquent leur middleware Bearer JWT habituel ; les handlers extraient Claims via l'extracteur existant Auth(Claims) côté b.tld, et acceptent un Identity::Remote côté IdP. Une vérification que l'identity correspond bien au handle de l'invite est faite par le service.
  • Pas de listing pagination — list_pending_for_team retourne tous les pending. Pagination différée si besoin (volume faible attendu).
  • Pas de routes resource-share (shared_with_remote_identity_id) — un follow-up RFC ajoutera l'endpoint quand resource_service::share_with_remote_identity sera livré.

API proposée

Bodies / responses JSON

// POST /api/v1/teams/{team_id}/members/invite-fediverse
#[derive(Deserialize)]
pub struct InviteBody { pub handle: String, pub role: String /* "member"|"admin" */ }
#[derive(Serialize)]
pub struct InviteResponse { pub invite_id: Uuid, pub activity_id: String, pub expires_at: String }

// GET /api/v1/teams/{team_id}/invites
#[derive(Serialize)]
pub struct InviteRow {
    pub id: Uuid, pub handle: String, pub role: String, pub status: String,
    pub created_at: String, pub expires_at: String,
}

// IdP-side accept/reject — empty body, just a path id.

Câblage

fediverse_invite_routes::router(state) ajouté au routes::router_with_auth après auth_routes::router. Pas de nouvel Extension requis : tous les handlers utilisent le DatabaseConnection injecté.

États HTTP

EndpointSuccèsErreurs
invite-fediverse201 Created400 handle/role invalide
invites (list)200 OK—
invites/{id}/revoke200 OK404 invite inconnu, 400 non-revocable
/fediverse/invites (IdP)200 OK—
accept200 OK404, 400 non-pending
reject200 OK404, 400 non-pending

Analyse de compatibilité

  • Non-breaking : 6 nouveaux paths gated par experimental-fediverse.
  • Aucune modification de l'API team_service au-delà de RFC 0009.
  • Aucun changement de schéma DB.

Alternatives considérées

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

Pros : conforme aux conventions existantes ; chaque endpoint a un audit clair.

Cons : 6 paths à mémoriser.

Alternative B — Endpoint unique /api/v1/fediverse/invites/{id} avec champ decision

Cons : pollue le path avec une logique d'action ; rate-limit / audit moins clair.

Rejeté.

Alternative C — IdP-side via le même path que côté admin

Cons : confusion de responsabilités, erreurs d'authz.

Rejeté.

Alternative D — Ne rien faire

Cons : Phase 3 reste théorique côté HTTP.

Rejeté.

Testing plan

12 tests :

  1. invite_returns_201_with_invite_id.
  2. invite_returns_400_for_invalid_role.
  3. list_pending_returns_200_with_array.
  4. revoke_returns_200_for_pending_invite.
  5. revoke_returns_404_for_unknown_invite.
  6. idp_list_returns_200_with_invites_for_remote_identity.
  7. idp_accept_returns_200_for_pending.
  8. idp_accept_returns_400_for_already_accepted.
  9. idp_reject_returns_200_for_pending.
  10. idp_reject_returns_400_for_already_decided.
  11. invite_uses_default_ttl_from_fediverse_config.
  12. idp_list_returns_empty_array_when_no_invites.

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

Migration guide

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

- 6 routes HTTP : 3 admin + 3 IdP.
- Module `rustwarden_core::fediverse::invite_routes`.
- Bodies / responses : `InviteBody`, `InviteResponse`, `InviteRow`.

### Migration steps

1. Aucun changement obligatoire — gating feature.
2. Apps consommatrices : peuvent maintenant piloter le flow
   invitation via JSON depuis n'importe quel SPA / app mobile.

Open questions

Toutes résolues à l'auto-revue :

  • TTL invitations : 7 jours par défaut (paramétrable via FediverseConfig plus tard, hors scope ici).
  • Path IdP : /api/v1/fediverse/invites (pas /api/v1/me/invites pour rester cohérent avec les autres routes fediverse).
  • Pagination : différée. Si nécessaire, ajouter ?cursor=…&limit=… dans une RFC suivante.

Tracking issue

À ouvrir au moment de la PR.


Score d'évaluation

CritèreNote (1-5)Commentaire
Généralité4Pattern réutilisable pour d'autres types d'invitations (resource-share).
Stabilité API4Additif, gated.
Performance4Lookups indexés (fediverse_pending_invites_status_idx).
Sécurité3Authz déléguée aux apps consommatrices via leur middleware Bearer.
Complexité d'implémentation4~500 lignes (6 handlers + state + 12 tests).
Maintenance36 paths à conserver compatibles.
Documentation4Rustdoc EN, doctest, CHANGELOG, migration guide.
Total26/35≥ 25 → Accepted