API Invitations — Vérification & Acceptation
Endpoints verify et accept : implémentation de la page d'acceptation, effets serveur et pièges.
API Invitations — Vérification & Acceptation
Ce chapitre couvre les endpoints publics
verifyetaccept. Lisez Invitations — Création d'abord pour le contexte.
Endpoint — Vérifier un token (PUBLIC)
À appeler depuis votre page d'acceptation au chargement, avant d'afficher le formulaire.
httpGET /api/invitations/verify/{token}
Auth : Aucune — endpoint public. Throttle : 10 req/min/IP.
Réponse valide (200) :
json{ "valid": true, "invitation": { "id": "uuid", "invitedEmail": "alice@acme.com", "invitedFirstName": "Alice", "invitedLastName": "Martin", "expiresAt": "2026-05-29T10:30:00.000Z", "customer": { "name": "Acme Corp" }, "profile": { "name": "Participant", "app": { "name": "MonApp" } }, "inviter": { "firstName": "Bob", "lastName": "Smith", "email": "bob@..." } } }
Réponse invalide (200 mais valid: false) :
json{ "valid": false, "errorMessage": "Cette invitation a expiré" }
errorMessage possibles :
"Invitation non trouvée""Cette invitation a déjà été acceptée""Cette invitation a expiré""Cette invitation a été annulée"
Si le token est
PENDINGmais queexpiresAt < now(), l'API marque automatiquement l'invitationEXPIREDavant de répondre.
Pattern React — Page d'acceptation
typescript// app/accept-invitation/[token]/page.tsx async function AcceptInvitationPage({ params }: { params: { token: string } }) { const res = await fetch(`/api/proxy/invitations/verify/${params.token}`); const { valid, invitation, errorMessage } = await res.json(); if (!valid) { return <InvitationErrorPage message={errorMessage} />; } return <AcceptInvitationForm invitation={invitation} token={params.token} />; }
Composant d'erreur recommandé :
typescriptfunction InvitationErrorPage({ message }: { message: string }) { const messages: Record<string, { title: string; action: string }> = { "Cette invitation a expiré": { title: "Ce lien a expiré", action: "Demandez un nouveau lien à l'administrateur.", }, "Cette invitation a déjà été acceptée": { title: "Compte déjà créé", action: "Connectez-vous directement avec votre email.", }, "Cette invitation a été annulée": { title: "Invitation annulée", action: "Contactez l'administrateur pour obtenir une nouvelle invitation.", }, "Invitation non trouvée": { title: "Lien invalide", action: "Ce lien d'invitation n'existe pas. Vérifiez l'URL.", }, }; const info = messages[message] ?? { title: "Erreur", action: "Contactez le support." }; return ( <div className="flex flex-col items-center gap-4 p-8"> <h1 className="text-2xl font-bold">{info.title}</h1> <p className="text-muted-foreground">{info.action}</p> </div> ); }
Endpoint — Accepter une invitation (PUBLIC)
httpPOST /api/invitations/accept/{token} Content-Type: application/json { "password": "MotDePasse2026!" }
Auth : Aucune — endpoint public. Throttle : 5 req/min/IP.
Réponse 200 :
json{ "id": "uuid", "wid": "abc123", "email": "alice@acme.com", "firstName": "Alice", "lastName": "Martin", "customerId": "uuid", "profileId": "uuid", "adminLevel": "USER", "isActive": true, "keycloakId": "uuid-keycloak" }
Effets côté serveur :
- Création du compte Keycloak dans le realm du Network
- Création de l'utilisateur en base (
adminLevel: USER,userLevel: USER— forcés) - Création des
UserTeampour chaqueteamAssignment - Marquage de l'invitation
ACCEPTED - Lien UserApp (user × Customer × App) créé
- Envoi du mail Keycloak de configuration du mot de passe
Note : le champ
passworddans le body est documenté mais actuellement ignoré côté service — Keycloak envoie un email de reset password séparément.
Exemple curl — flux complet
bash# 1. Votre backend crée l'invitation curl -X POST https://api.wakastart.app/api/invitations \ -H "x-api-key: sk_live_yourkey" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-1111-1111-111111111111", "invitedEmail": "alice@acme.com", "profileId": "22222222-2222-2222-2222-222222222222", "language": "fr" }' # 2. L'invité clique le lien, votre page vérifie le token curl https://api.wakastart.app/api/invitations/verify/a3f8c2d1e4b7... # → 200 { valid: true, invitation: { invitedEmail, ... } } # 3. L'invité soumet le formulaire curl -X POST https://api.wakastart.app/api/invitations/accept/a3f8c2d1e4b7... \ -H "Content-Type: application/json" \ -d '{ "password": "MotDePasseSolide2026!" }' # → 200 { user créé }
Bonnes pratiques
- Toujours appeler
verifyau mount avant d'afficher le formulaire — évite à l'invité de saisir un mot de passe pour rien. - Gérer les 4 états d'erreur avec des messages explicites adaptés.
Pièges classiques
- Appeler
acceptdeux fois : après acceptation, le token est invalidé. Le deuxième appel retourne400 Cette invitation a déjà été acceptée. - Oublier le
verifyinitial : l'invité remplit le formulaire, l'API répond400 expiré, mauvaise UX. userLevelforcé àUSER: les invitations ne peuvent pas promouvoir un utilisateur en admin. Les droits admin s'accordent manuellement après l'acceptation.- Throttle sur
accept: 5 req/min/IP. Un bot qui tente de brute-forcer des tokens sera bloqué après 5 essais.
Aller plus loin
- Invitations — Création : endpoint POST, pré-requis, cycle de vie
- Erreurs métier : traitement des messages
valid: false - Contrôle d'accès : droit
invitations.createet feature cascade