581 lines
15 KiB
Markdown
581 lines
15 KiB
Markdown
# API utilisateurs pour applications externes
|
|
|
|
Ce document decrit les endpoints exposes par ChessCubing pour authentifier un utilisateur, recuperer son profil, rechercher d'autres joueurs, gerer les relations sociales, et administrer les comptes.
|
|
|
|
Les exemples ci-dessous utilisent `http://localhost:8080` comme URL locale. En production, remplacez cette base par l'URL de votre instance.
|
|
|
|
## Vue d'ensemble
|
|
|
|
- API HTTP REST en JSON.
|
|
- Authentification par cookie de session, pas par bearer token applicatif.
|
|
- Cookie principal : `chesscubing.auth`
|
|
- Duree de vie du cookie : 30 jours, avec renouvellement glissant.
|
|
- Les dates sont retournees en UTC, au format ISO 8601.
|
|
- Les applications navigateur sur un autre domaine ne peuvent pas consommer cette API directement aujourd'hui : aucune politique CORS n'est configuree cote serveur. Pour une integration externe, privilegier un appel serveur a serveur, ou ajouter CORS dans l'application.
|
|
|
|
## Authentification
|
|
|
|
### Principe
|
|
|
|
1. L'application externe appelle `POST /api/auth/login`.
|
|
2. Le serveur renvoie un cookie HTTP-only.
|
|
3. Les appels suivants doivent reutiliser ce cookie.
|
|
|
|
Exemple `curl` avec conservation du cookie :
|
|
|
|
```bash
|
|
curl -sS -c cookies.txt \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"kiki","password":"motdepasse"}' \
|
|
http://localhost:8080/api/auth/login
|
|
```
|
|
|
|
Puis reutilisation du cookie :
|
|
|
|
```bash
|
|
curl -sS -b cookies.txt http://localhost:8080/api/users/me
|
|
```
|
|
|
|
### POST /api/auth/login
|
|
|
|
Authentifie un utilisateur Keycloak, cree si besoin son profil site, puis ouvre une session cookie.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"username": "kiki",
|
|
"password": "motdepasse"
|
|
}
|
|
```
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"isAuthenticated": true,
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"name": "Christophe JEANNEROT",
|
|
"email": "christophe@jeannerot.fr",
|
|
"roles": ["player", "admin"]
|
|
}
|
|
```
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` si `username` ou `password` sont absents.
|
|
- `401 Unauthorized` si les identifiants sont invalides.
|
|
- `5xx` ou autre code issu de Keycloak si l'authentification amont est indisponible.
|
|
|
|
### POST /api/auth/register
|
|
|
|
Cree un compte Keycloak, attribue le role `player`, connecte l'utilisateur et initialise son profil site.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"username": "kiki",
|
|
"email": "christophe@jeannerot.fr",
|
|
"password": "motdepasse123",
|
|
"confirmPassword": "motdepasse123",
|
|
"firstName": "Christophe",
|
|
"lastName": "JEANNEROT"
|
|
}
|
|
```
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"isAuthenticated": true,
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"name": "Christophe JEANNEROT",
|
|
"email": "christophe@jeannerot.fr",
|
|
"roles": ["player"]
|
|
}
|
|
```
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` si `username`, `email` ou `password` sont absents.
|
|
- `400 Bad Request` si `password` et `confirmPassword` different.
|
|
- `409 Conflict` si le nom d'utilisateur ou l'email existe deja.
|
|
|
|
### GET /api/auth/session
|
|
|
|
Retourne l'etat de session courant. Cette route est publique.
|
|
|
|
Si l'utilisateur est connecte :
|
|
|
|
```json
|
|
{
|
|
"isAuthenticated": true,
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"name": "Christophe JEANNEROT",
|
|
"email": "christophe@jeannerot.fr",
|
|
"roles": ["player", "admin"]
|
|
}
|
|
```
|
|
|
|
Si l'utilisateur n'est pas connecte :
|
|
|
|
```json
|
|
{
|
|
"isAuthenticated": false,
|
|
"subject": null,
|
|
"username": null,
|
|
"name": null,
|
|
"email": null,
|
|
"roles": []
|
|
}
|
|
```
|
|
|
|
### POST /api/auth/logout
|
|
|
|
Ferme la session courante et vide le cookie d'authentification.
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"isAuthenticated": false,
|
|
"subject": null,
|
|
"username": null,
|
|
"name": null,
|
|
"email": null,
|
|
"roles": []
|
|
}
|
|
```
|
|
|
|
### GET /api/auth/logout/browser
|
|
|
|
Equivalent navigateur de la deconnexion. Cette route supprime le cookie puis redirige vers `/index.html`.
|
|
|
|
## Profil du compte courant
|
|
|
|
Ces routes permettent a une application connectee de recuperer et modifier le profil site de l'utilisateur courant.
|
|
|
|
### GET /api/users/me
|
|
|
|
Necessite une session authentifiee.
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"email": "christophe@jeannerot.fr",
|
|
"displayName": "Christophe JEANNEROT",
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"preferredFormat": "Les deux",
|
|
"favoriteCube": "GAN 12",
|
|
"bio": "Joueur et organisateur.",
|
|
"createdUtc": "2026-04-14T18:04:55.0000000Z",
|
|
"updatedUtc": "2026-04-14T18:05:12.0000000Z"
|
|
}
|
|
```
|
|
|
|
Comportement notable :
|
|
|
|
- Si le profil site n'existe pas encore, il est cree automatiquement.
|
|
- `401 Unauthorized` si aucun cookie valide n'est fourni.
|
|
|
|
### PUT /api/users/me
|
|
|
|
Met a jour uniquement le profil site de l'utilisateur courant.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"displayName": "Christophe JEANNEROT",
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"preferredFormat": "Les deux",
|
|
"favoriteCube": "GAN 12",
|
|
"bio": "Joueur et organisateur."
|
|
}
|
|
```
|
|
|
|
Reponse `200 OK` : meme structure que `GET /api/users/me`.
|
|
|
|
Contraintes de validation :
|
|
|
|
- `displayName`, `club`, `city`, `favoriteCube` : 120 caracteres max.
|
|
- `bio` : 1200 caracteres max.
|
|
- `preferredFormat` : `Twice`, `Time` ou `Les deux`.
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` si la valeur de `preferredFormat` est invalide.
|
|
- `400 Bad Request` si une longueur maximale est depassee.
|
|
- `401 Unauthorized` sans session.
|
|
|
|
## Recherche de joueurs et relations sociales
|
|
|
|
Ces routes necessitent toutes une session authentifiee.
|
|
|
|
### GET /api/social/overview
|
|
|
|
Retourne la vue sociale du compte courant :
|
|
|
|
- `friends`
|
|
- `receivedInvitations`
|
|
- `sentInvitations`
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"friends": [
|
|
{
|
|
"subject": "sub-ami-1",
|
|
"username": "alex",
|
|
"displayName": "Alex Martin",
|
|
"email": "alex@example.com",
|
|
"club": "Club A",
|
|
"city": "Paris",
|
|
"isOnline": true
|
|
}
|
|
],
|
|
"receivedInvitations": [
|
|
{
|
|
"invitationId": 12,
|
|
"subject": "sub-ami-2",
|
|
"username": "lea",
|
|
"displayName": "Lea Durand",
|
|
"email": "lea@example.com",
|
|
"isOnline": false,
|
|
"createdUtc": "2026-04-15T08:15:00.0000000Z"
|
|
}
|
|
],
|
|
"sentInvitations": [
|
|
{
|
|
"invitationId": 18,
|
|
"subject": "sub-ami-3",
|
|
"username": "nina",
|
|
"displayName": "Nina Bernard",
|
|
"email": "nina@example.com",
|
|
"isOnline": true,
|
|
"createdUtc": "2026-04-15T08:18:00.0000000Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### GET /api/social/search?query={texte}
|
|
|
|
Recherche des joueurs connus du site par nom d'utilisateur, nom affiche, club, ville ou email selon l'index applicatif.
|
|
|
|
Exemple :
|
|
|
|
```bash
|
|
curl -sS -b cookies.txt \
|
|
"http://localhost:8080/api/social/search?query=chri"
|
|
```
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
[
|
|
{
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"displayName": "Christophe JEANNEROT",
|
|
"email": "christophe@jeannerot.fr",
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"isOnline": true,
|
|
"isFriend": false,
|
|
"hasSentInvitation": false,
|
|
"hasReceivedInvitation": true
|
|
}
|
|
]
|
|
```
|
|
|
|
Comportement notable :
|
|
|
|
- Si `query` est vide ou blanche, la route renvoie `[]`.
|
|
- La recherche exige au moins 2 caracteres utiles.
|
|
- Le resultat est limite a 12 utilisateurs.
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` si `query` contient moins de 2 caracteres.
|
|
- `401 Unauthorized` sans session.
|
|
|
|
### POST /api/social/invitations
|
|
|
|
Envoie une invitation d'ami.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"targetSubject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28"
|
|
}
|
|
```
|
|
|
|
Reponse `204 No Content`
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` si on tente de s'inviter soi-meme.
|
|
- `400 Bad Request` si le joueur cible est introuvable.
|
|
- `400 Bad Request` si le joueur est deja ami.
|
|
- `400 Bad Request` si une invitation entre les deux comptes existe deja.
|
|
|
|
### POST /api/social/invitations/{invitationId}/accept
|
|
|
|
Accepte une invitation recue.
|
|
|
|
Reponse `204 No Content`
|
|
|
|
Erreur frequente :
|
|
|
|
- `400 Bad Request` si l'invitation est introuvable ou deja traitee.
|
|
|
|
### POST /api/social/invitations/{invitationId}/decline
|
|
|
|
Refuse une invitation recue.
|
|
|
|
Reponse `204 No Content`
|
|
|
|
Erreur frequente :
|
|
|
|
- `400 Bad Request` si l'invitation est introuvable ou deja traitee.
|
|
|
|
### DELETE /api/social/invitations/{invitationId}
|
|
|
|
Annule une invitation envoyee.
|
|
|
|
Reponse `204 No Content`
|
|
|
|
Erreur frequente :
|
|
|
|
- `400 Bad Request` si l'invitation est introuvable ou deja retiree.
|
|
|
|
### DELETE /api/social/friends/{friendSubject}
|
|
|
|
Supprime une relation d'amitie.
|
|
|
|
Reponse `204 No Content`
|
|
|
|
## Administration des utilisateurs
|
|
|
|
Ces routes sont reservees aux sessions portant le role `admin`.
|
|
|
|
Sans ce role :
|
|
|
|
- `401 Unauthorized` si l'utilisateur n'est pas connecte.
|
|
- `403 Forbidden` si l'utilisateur est connecte mais n'a pas le role `admin`.
|
|
|
|
### GET /api/admin/users
|
|
|
|
Retourne toute la liste des utilisateurs connus, en fusionnant :
|
|
|
|
- les comptes Keycloak
|
|
- les profils site MySQL
|
|
|
|
La reponse est triee par activite recente, puis par nom d'utilisateur.
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
[
|
|
{
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"email": "christophe@jeannerot.fr",
|
|
"identityDisplayName": "Christophe JEANNEROT",
|
|
"siteDisplayName": "Christophe JEANNEROT",
|
|
"isEnabled": true,
|
|
"isEmailVerified": false,
|
|
"hasSiteProfile": true,
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"preferredFormat": "Les deux",
|
|
"accountCreatedUtc": "2026-04-14T17:44:00.0000000Z",
|
|
"siteProfileUpdatedUtc": "2026-04-14T18:05:00.0000000Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
### GET /api/admin/users/{subject}
|
|
|
|
Retourne le detail complet d'un utilisateur.
|
|
|
|
Reponse `200 OK` :
|
|
|
|
```json
|
|
{
|
|
"subject": "2f7d0f1d-3ef6-4b5f-aab5-4cf6b61c0a28",
|
|
"username": "kiki",
|
|
"email": "christophe@jeannerot.fr",
|
|
"firstName": "Christophe",
|
|
"lastName": "JEANNEROT",
|
|
"identityDisplayName": "Christophe JEANNEROT",
|
|
"isEnabled": true,
|
|
"isEmailVerified": false,
|
|
"accountCreatedUtc": "2026-04-14T17:44:00.0000000Z",
|
|
"hasSiteProfile": true,
|
|
"displayName": "Christophe JEANNEROT",
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"preferredFormat": "Les deux",
|
|
"favoriteCube": "GAN 12",
|
|
"bio": "Joueur et organisateur.",
|
|
"siteProfileCreatedUtc": "2026-04-14T18:04:00.0000000Z",
|
|
"siteProfileUpdatedUtc": "2026-04-14T18:05:00.0000000Z"
|
|
}
|
|
```
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `404 Not Found` si l'utilisateur n'existe pas dans Keycloak.
|
|
|
|
### POST /api/admin/users
|
|
|
|
Cree un utilisateur, son mot de passe, puis son profil site.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"username": "nouveau",
|
|
"email": "nouveau@example.com",
|
|
"password": "motdepasse123",
|
|
"confirmPassword": "motdepasse123",
|
|
"firstName": "Nouveau",
|
|
"lastName": "Joueur",
|
|
"isEnabled": true,
|
|
"isEmailVerified": false,
|
|
"displayName": "Nouveau Joueur",
|
|
"club": "Club A",
|
|
"city": "Paris",
|
|
"preferredFormat": "Time",
|
|
"favoriteCube": "Moyu RS3M",
|
|
"bio": "Nouveau profil"
|
|
}
|
|
```
|
|
|
|
Reponse `201 Created` :
|
|
|
|
- En-tete `Location: /api/admin/users/{subject}`
|
|
- Corps : meme structure que `GET /api/admin/users/{subject}`
|
|
|
|
Contraintes de validation :
|
|
|
|
- `username` obligatoire, max 120 caracteres.
|
|
- `email` facultatif mais valide si fourni, max 255 caracteres.
|
|
- `password` obligatoire, minimum 8 caracteres.
|
|
- `confirmPassword` doit correspondre a `password`.
|
|
- `firstName`, `lastName`, `displayName`, `club`, `city`, `favoriteCube` : 120 caracteres max.
|
|
- `bio` : 1200 caracteres max.
|
|
- `preferredFormat` : `Twice`, `Time` ou `Les deux`.
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` sur validation metier.
|
|
- `409 Conflict` si le nom d'utilisateur ou l'email existe deja.
|
|
|
|
### PUT /api/admin/users/{subject}
|
|
|
|
Met a jour le compte Keycloak et le profil site.
|
|
|
|
Important :
|
|
|
|
- Le `username` n'est pas modifiable via cette route.
|
|
|
|
Requete :
|
|
|
|
```json
|
|
{
|
|
"email": "christophe@jeannerot.fr",
|
|
"firstName": "Christophe",
|
|
"lastName": "JEANNEROT",
|
|
"isEnabled": true,
|
|
"isEmailVerified": true,
|
|
"displayName": "Christophe JEANNEROT",
|
|
"club": "ChessCubing Arena",
|
|
"city": "Vercel Villedieu le camp",
|
|
"preferredFormat": "Les deux",
|
|
"favoriteCube": "GAN 12",
|
|
"bio": "Joueur et organisateur."
|
|
}
|
|
```
|
|
|
|
Reponse `200 OK` : meme structure que `GET /api/admin/users/{subject}`.
|
|
|
|
Erreurs frequentes :
|
|
|
|
- `400 Bad Request` sur validation metier.
|
|
- `404 Not Found` si l'utilisateur est introuvable.
|
|
- `409 Conflict` si l'email est deja utilise par un autre compte.
|
|
|
|
### DELETE /api/admin/users/{subject}
|
|
|
|
Supprime :
|
|
|
|
- le compte Keycloak
|
|
- le profil site
|
|
- les relations sociales et invitations associees
|
|
|
|
Reponse `204 No Content`
|
|
|
|
Erreur frequente :
|
|
|
|
- `404 Not Found` si l'utilisateur est introuvable.
|
|
|
|
## Temps reel via SignalR
|
|
|
|
Le hub `/hubs/social` est protege par authentification et permet :
|
|
|
|
- la presence en ligne
|
|
- les invitations de partie entre amis
|
|
- la synchronisation d'une partie entre plusieurs devices
|
|
|
|
Les principaux messages/calls utilises sont :
|
|
|
|
- appel client -> serveur `RequestPresenceSnapshot`
|
|
- appel client -> serveur `SendPlayInvite(recipientSubject, recipientColor)`
|
|
- appel client -> serveur `RespondToPlayInvite(inviteId, accept)`
|
|
- appel client -> serveur `CancelPlayInvite(inviteId)`
|
|
- appel client -> serveur `JoinPlaySession(sessionId)`
|
|
- appel client -> serveur `LeavePlaySession(sessionId)`
|
|
- appel client -> serveur `PublishMatchState(sessionId, matchJson, route)`
|
|
- evenement serveur -> client `PresenceSnapshot`
|
|
- evenement serveur -> client `PresenceChanged`
|
|
- evenement serveur -> client `PlayInviteUpdated`
|
|
- evenement serveur -> client `PlayInviteAccepted`
|
|
- evenement serveur -> client `PlayInviteClosed`
|
|
- evenement serveur -> client `CollaborativeMatchStateUpdated`
|
|
|
|
Pour un integrateur qui veut seulement recuperer les informations utilisateur, les endpoints REST de ce document sont generalement suffisants. Le hub devient utile des qu'il faut suivre la presence, les invitations ou l'etat partage d'une partie en temps reel.
|
|
|
|
## Codes de reponse a prevoir
|
|
|
|
- `200 OK` : lecture ou action aboutie avec corps JSON.
|
|
- `201 Created` : creation d'utilisateur admin.
|
|
- `204 No Content` : action aboutie sans corps.
|
|
- `400 Bad Request` : erreur de validation ou session incomplete.
|
|
- `401 Unauthorized` : pas de session valide.
|
|
- `403 Forbidden` : session valide mais role insuffisant.
|
|
- `404 Not Found` : ressource introuvable, principalement cote admin.
|
|
- `409 Conflict` : doublon sur `username` ou `email`.
|
|
|
|
## Recommandations d'integration
|
|
|
|
- Stocker et rejouer le cookie de session si l'application externe fonctionne en backend.
|
|
- Eviter les appels directs depuis un frontend sur un autre domaine tant que CORS n'est pas configure.
|
|
- Utiliser `GET /api/auth/session` pour verifier rapidement l'etat de connexion et les roles.
|
|
- Utiliser `GET /api/users/me` pour recuperer le profil courant.
|
|
- Utiliser `GET /api/social/search` si l'objectif est de rechercher des joueurs connectes au site.
|
|
- Utiliser `GET /api/admin/users` uniquement pour une application d'administration portant le role `admin`.
|