# 📚 Documentation ComplĂšte API mcV3 ## Table des matiĂšres 1. [Architecture gĂ©nĂ©rale](#architecture-gĂ©nĂ©rale) 2. [Authentification](#authentification) 3. [Endpoints API](#endpoints-api) 4. [Gestion des clĂ©s API](#gestion-des-clĂ©s-api) 5. [RĂŽles et permissions](#rĂŽles-et-permissions) 6. [Codes HTTP et erreurs](#codes-http-et-erreurs) 7. [IntĂ©grations](#intĂ©grations) 8. [Configuration avancĂ©e](#configuration-avancĂ©e) 9. [FAQ et dĂ©pannage](#faq-et-dĂ©pannage) --- ## Architecture gĂ©nĂ©rale ### Vue d'ensemble Votre API utilise une **authentification hybride** combinant deux modes: 1. **Token API (Bearer Token)** - Pour applications externes/services 2. **Session utilisateur** - Pour utilisateurs connectĂ©s au site web ``` ┌─────────────────────────────────────────────┐ │ RequĂȘte /api/* │ └────────────────┬────────────────────────────┘ │ ┌───────┮──────────┐ │ │ ┌────▌─────┐ ┌───────▌──────┐ │ Bearer │ │ Session │ │ Token? │ │ Active? │ └────┬─────┘ └───────┬──────┘ │ │ ┌────▌──────────────────▌────┐ │ ApiTokenHandler OR │ │ Session Storage │ └────┬──────────────────────┘ │ ┌────▌──────────────────────┐ │ Load User + Roles │ └────┬──────────────────────┘ │ ┌────▌──────────────────────┐ │ Verify @IsGranted() │ └────┬──────────────────────┘ │ ✅ 200 OK ou ❌ 401/403 ``` ### Flux de sĂ©curitĂ© 1. **Firewall `/api`** - Accepte les requĂȘtes `/api/*` 2. **ApiBearerTokenAuthenticator** - VĂ©rifie les Bearer tokens 3. **http_basic** - Charge la session du context partagĂ© 4. **ContrĂŽles d'accĂšs** - VĂ©rifie `@IsGranted()` sur les routes 5. **ApiExceptionListener** - Convertit les erreurs en JSON --- ## Authentification ### Mode 1: Bearer Token (ClĂ© API) #### GĂ©nĂ©ration du token **Via CLI:** ```bash php bin/console app:api-key:create pseudo_utilisateur "Nom de l'application" ``` Le token est gĂ©nĂ©rĂ© automatiquement (64 caractĂšres hexadĂ©cimaux, cryptographiquement sĂ©curisĂ©). **Via API (utilisateur connectĂ©):** ```http POST /api/api-keys Content-Type: application/json Authorization: Bearer existing_token { "name": "Application mobile", "expiresAt": "2027-04-03T23:59:59Z" // optionnel } ``` #### Utilisation du token Le token doit ĂȘtre envoyĂ© dans l'en-tĂȘte `Authorization`: ```http GET /api/profile Authorization: Bearer abc123def456... ``` #### CaractĂ©ristiques - ✅ Unique par clĂ© API - ✅ Peut avoir une date d'expiration - ✅ Peut ĂȘtre dĂ©sactivĂ©/rĂ©voquĂ© - ✅ Stateless (pas de session serveur) - ✅ Audit (tracking du dernier accĂšs) ### Mode 2: Session utilisateur #### Établir une session ```http POST /login Content-Type: application/x-www-form-urlencoded _username=john&_password=secret ``` Un cookie `PHPSESSID` est créé et stockĂ©. #### Utiliser la session Tous les cookies sont envoyĂ©s automatiquement par le navigateur. Via cURL: ```bash curl -b cookies.txt -X POST http://localhost:8000/login \ -d "_username=john&_password=secret" curl -b cookies.txt http://localhost:8000/api/profile ``` #### CaractĂ©ristiques - ✅ BasĂ©e sur les cookies - ✅ Automatique dans le navigateur - ✅ Partage avec le firewall `main` - ✅ Timeout configurable - ✅ Plus sĂ©curisĂ©e pour les applications web ### DĂ©tection du mode d'authentification La rĂ©ponse `/api/profile` indique le mode utilisĂ©: ```json { "id": 1, "pseudo": "john", "roles": ["ROLE_USER"], "authenticatedVia": "api_key" // ou "session" } ``` --- ## Endpoints API ### GET /api/health **Authentification**: ❌ Non requise VĂ©rifier l'Ă©tat de l'API. **Exemple:** ```bash curl http://localhost:8000/api/health ``` **RĂ©ponse (200 OK):** ```json { "status": "ok", "timestamp": "2026-04-03T10:30:00+00:00" } ``` --- ### GET /api/profile **Authentification**: ✅ Requise (Bearer token ou session) RĂ©cupĂ©rer le profil de l'utilisateur authentifiĂ©. **Exemple avec Bearer token:** ```bash curl -X GET http://localhost:8000/api/profile \ -H "Authorization: Bearer your_token" ``` **RĂ©ponse (200 OK):** ```json { "id": 1, "pseudo": "john", "roles": ["ROLE_USER", "ROLE_ADMIN"], "authenticatedVia": "api_key" } ``` **Erreurs possibles:** - `401 Unauthorized` - Token invalide/expirĂ© ou pas de session --- ### GET /api/api-keys **Authentification**: ✅ Requise (Bearer token ou session) Lister les clĂ©s API. - **ROLE_USER**: Voit uniquement SES propres clĂ©s - **ROLE_ADMIN**: Voit TOUTES les clĂ©s du systĂšme **Exemple:** ```bash curl -X GET http://localhost:8000/api/api-keys \ -H "Authorization: Bearer your_token" ``` **RĂ©ponse (200 OK):** ```json { "apiKeys": [ { "id": 1, "name": "Mobile App", "token": "a1b2c3d4e5...", // masquĂ© pour sĂ©curitĂ© "createdAt": "2026-04-01T10:00:00+00:00", "lastUsedAt": "2026-04-03T15:30:00+00:00", "expiresAt": "2026-12-31T23:59:59+00:00", "isActive": true } ] } ``` --- ### POST /api/api-keys **Authentification**: ✅ Requise (Bearer token ou session) CrĂ©er une nouvelle clĂ© API. **Body (JSON):** ```json { "name": "Nouvelle application", "expiresAt": "2027-04-03T23:59:59Z" // optionnel } ``` **Exemple:** ```bash curl -X POST http://localhost:8000/api/api-keys \ -H "Authorization: Bearer your_token" \ -H "Content-Type: application/json" \ -d '{ "name": "API Desktop", "expiresAt": "2026-12-31T23:59:59Z" }' ``` **RĂ©ponse (201 Created):** ```json { "id": 5, "name": "API Desktop", "token": "abc123def456...xyz", // Complet Ă  la crĂ©ation uniquement! "createdAt": "2026-04-03T16:45:00+00:00", "message": "Copier le token en lieu sĂ»r, il ne sera plus visible ultĂ©rieurement" } ``` **⚠ IMPORTANT**: Le token complet n'est affichĂ© qu'Ă  la crĂ©ation. Stockez-le immĂ©diatement. **Erreurs possibles:** - `400 Bad Request` - ParamĂštre "name" manquant - `401 Unauthorized` - Authentification invalide --- ### DELETE /api/api-keys/{id} **Authentification**: ✅ Requise (Bearer token ou session) Supprimer une clĂ© API. - **ROLE_USER**: Peut supprimer uniquement ses propres clĂ©s - **ROLE_ADMIN**: Peut supprimer n'importe quelle clĂ© **Exemple:** ```bash curl -X DELETE http://localhost:8000/api/api-keys/1 \ -H "Authorization: Bearer your_token" ``` **RĂ©ponse (200 OK):** ```json { "message": "API key deleted" } ``` **Erreurs possibles:** - `403 Forbidden` - Essai de supprimer la clĂ© d'un autre utilisateur - `404 Not Found` - ClĂ© inexistante --- ### PATCH /api/api-keys/{id}/deactivate **Authentification**: ✅ Requise (Bearer token ou session) DĂ©sactiver une clĂ© API sans la supprimer. **Exemple:** ```bash curl -X PATCH http://localhost:8000/api/api-keys/1/deactivate \ -H "Authorization: Bearer your_token" ``` **RĂ©ponse (200 OK):** ```json { "message": "API key deactivated" } ``` --- ## Gestion des clĂ©s API ### Cycle de vie d'une clĂ© ``` CrĂ©ation │ ├─→ Actif et utilisable │ ├─→ Peut ĂȘtre dĂ©sactivĂ© (sans suppression) │ ├─→ Peut avoir une expiration automatique │ └─→ Peut ĂȘtre supprimĂ© dĂ©finitivement ``` ### Bonnes pratiques 1. **CrĂ©er des clĂ©s par application** - Une clĂ© par service/app 2. **DĂ©finir une expiration** - Rotation annuelle recommandĂ©e 3. **RĂ©voquer rĂ©guliĂšrement** - Supprimer les vieilles clĂ©s 4. **Ne jamais partager** - Chaque clĂ© est personnelle 5. **Monitorer l'accĂšs** - VĂ©rifier `lastUsedAt` ### Exemple: Rotation de clĂ©s ```bash # 1. CrĂ©er une nouvelle clĂ© curl -X POST http://localhost:8000/api/api-keys \ -H "Authorization: Bearer old_token" \ -H "Content-Type: application/json" \ -d '{"name": "Mobile App v2"}' # 2. Mettre Ă  jour l'application avec la nouvelle clĂ© # ... dĂ©ployer avec new_token ... # 3. DĂ©sactiver l'ancienne clĂ© curl -X PATCH http://localhost:8000/api/api-keys/1/deactivate \ -H "Authorization: Bearer new_token" # 4. (Optionnel) Supprimer aprĂšs confirmation curl -X DELETE http://localhost:8000/api/api-keys/1 \ -H "Authorization: Bearer new_token" ``` --- ## RĂŽles et permissions ### HiĂ©rarchie de rĂŽles ``` ROLE_ADMIN └── ROLE_USER ROLE_MODERATOR └── ROLE_USER ROLE_USER (base) ``` ### Permissions par rĂŽle | Action | ROLE_USER | ROLE_ADMIN | |--------|-----------|-----------| | Voir son profil | ✅ | ✅ | | Voir ses clĂ©s | ✅ | ✅ | | Voir les clĂ©s d'autres | ❌ | ✅ | | CrĂ©er ses clĂ©s | ✅ | ✅ | | Supprimer ses clĂ©s | ✅ | ✅ | | Supprimer les clĂ©s d'autres | ❌ | ✅ | | AccĂ©der Ă  /api/* | ✅ | ✅ | | AccĂ©der Ă  /administration | ❌ | ✅ | ### ImplĂ©mentation dans le code ```php // ProtĂ©ger une route par rĂŽle #[Route('/api/mon-endpoint')] #[IsGranted('ROLE_USER')] public function monEndpoint(): JsonResponse { } // Logique conditionnelle selon le rĂŽle if ($this->isGranted('ROLE_ADMIN')) { // Admin seulement return $this->json($allData); } return $this->json($userData); ``` --- ## Codes HTTP et erreurs ### Codes de succĂšs | Code | Signification | Utilisation | |------|---------------|-------------| | 200 | OK | RequĂȘte rĂ©ussie (GET, POST, PATCH, DELETE) | | 201 | Created | Ressource créée (POST) | | 204 | No Content | SuccĂšs sans contenu (rare) | ### Codes d'erreur client (4xx) #### 400 Bad Request RequĂȘte invalide (paramĂštres manquants, JSON malformĂ©). **Exemple:** ```json { "error": "Bad Request", "message": "name is required" } ``` **Solutions:** - VĂ©rifier le format JSON - Ajouter les paramĂštres requis #### 401 Unauthorized Non authentifiĂ© ou authentification invalide. **Cas courants:** - Token manquant: `curl http://localhost:8000/api/profile` - Token invalide: `Authorization: Bearer wrong_token` - Token expirĂ©: Token dont la date d'expiration est passĂ©e - Session expirĂ©e: Cookies invalides/Ă©coulĂ©s **RĂ©ponse:** ```json { "error": "Unauthorized", "message": "Authentication required. Please provide a valid API token or be logged in." } ``` **Solutions:** - Ajouter le header `Authorization: Bearer YOUR_TOKEN` - Renouveler le token expirĂ© - Se reconnecter (pour session) #### 403 Forbidden AuthentifiĂ© mais permissions insuffisantes. **Cas courants:** - ROLE_USER essayant de supprimer la clĂ© d'un autre - AccĂšs Ă  un endpoint rĂ©servĂ© aux admins **RĂ©ponse:** ```json { "error": "Forbidden", "message": "You do not have permission to access this resource" } ``` **Solutions:** - Utiliser un compte admin si nĂ©cessaire - VĂ©rifier les droits requis #### 404 Not Found Ressource inexistante. **Cas courants:** - ClĂ© API inexistante: `DELETE /api/api-keys/99999` - Endpoint invalide: `GET /api/nonexistent` **Solutions:** - VĂ©rifier l'ID de la ressource - VĂ©rifier le chemin de la route ### Codes d'erreur serveur (5xx) #### 500 Internal Server Error Erreur serveur gĂ©nĂ©rique. **Cause:** Bug ou exception non gĂ©rĂ©e. **Solutions:** - VĂ©rifier les logs: `tail -f var/log/dev.log` - Contacter le dĂ©veloppeur --- ## IntĂ©grations ### JavaScript / Fetch API ```javascript // Configuration const API_BASE_URL = 'http://localhost:8000'; const API_TOKEN = 'your_api_token'; // Helper pour les appels API async function apiCall(endpoint, options = {}) { const url = `${API_BASE_URL}${endpoint}`; const headers = { 'Authorization': `Bearer ${API_TOKEN}`, 'Content-Type': 'application/json', ...options.headers, }; const response = await fetch(url, { ...options, headers, }); if (!response.ok) { const error = await response.json(); throw new Error(`${response.status}: ${error.message}`); } return response.json(); } // Utilisation async function getProfile() { try { const profile = await apiCall('/api/profile'); console.log('Profil:', profile); } catch (error) { console.error('Erreur:', error.message); } } async function createApiKey(name) { try { const key = await apiCall('/api/api-keys', { method: 'POST', body: JSON.stringify({ name }), }); console.log('ClĂ© créée:', key.token); } catch (error) { console.error('Erreur:', error.message); } } // Appels getProfile(); createApiKey('Mon App'); ``` ### Python / Requests ```python import requests import json class ApiClient: def __init__(self, base_url, api_token): self.base_url = base_url self.api_token = api_token self.session = requests.Session() self.session.headers.update({ 'Authorization': f'Bearer {api_token}', 'Content-Type': 'application/json', }) def request(self, method, endpoint, data=None): url = f"{self.base_url}{endpoint}" try: if method == 'GET': response = self.session.get(url) elif method == 'POST': response = self.session.post(url, json=data) elif method == 'DELETE': response = self.session.delete(url) elif method == 'PATCH': response = self.session.patch(url, json=data) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Erreur: {e}") return None def get_profile(self): return self.request('GET', '/api/profile') def list_keys(self): return self.request('GET', '/api/api-keys') def create_key(self, name, expires_at=None): data = {'name': name} if expires_at: data['expiresAt'] = expires_at return self.request('POST', '/api/api-keys', data) # Utilisation client = ApiClient('http://localhost:8000', 'your_api_token') profile = client.get_profile() print(f"Profil: {profile}") keys = client.list_keys() print(f"ClĂ©s: {keys}") new_key = client.create_key('Python App') print(f"Nouvelle clĂ©: {new_key['token']}") ``` ### PHP / cURL ```php class ApiClient { private $baseUrl; private $apiToken; public function __construct($baseUrl, $apiToken) { $this->baseUrl = rtrim($baseUrl, '/'); $this->apiToken = $apiToken; } private function request($method, $endpoint, $data = null) { $url = $this->baseUrl . $endpoint; $options = [ 'http' => [ 'method' => $method, 'header' => [ "Authorization: Bearer {$this->apiToken}", 'Content-Type: application/json', ], 'timeout' => 10, ], ]; if ($data && in_array($method, ['POST', 'PATCH'])) { $options['http']['content'] = json_encode($data); } $context = stream_context_create($options); $response = @file_get_contents($url, false, $context); if ($response === false) { return ['error' => 'Erreur de connexion']; } return json_decode($response, true); } public function getProfile() { return $this->request('GET', '/api/profile'); } public function listKeys() { return $this->request('GET', '/api/api-keys'); } public function createKey($name, $expiresAt = null) { $data = ['name' => $name]; if ($expiresAt) { $data['expiresAt'] = $expiresAt; } return $this->request('POST', '/api/api-keys', $data); } } // Utilisation $client = new ApiClient('http://localhost:8000', 'your_api_token'); $profile = $client->getProfile(); var_dump($profile); $keys = $client->listKeys(); var_dump($keys); $newKey = $client->createKey('PHP App'); echo "Nouvelle clĂ©: " . $newKey['token']; ``` --- ## Configuration avancĂ©e ### Ajouter des endpoints personnalisĂ©s ```php // Dans src/Controller/ApiController.php #[Route('/api/custom-endpoint')] #[IsGranted('ROLE_USER')] public function customEndpoint(): JsonResponse { $user = $this->getUser(); return $this->json([ 'message' => 'DonnĂ©es personnalisĂ©es', 'user' => $user->getPseudo(), ]); } ``` ### Filtrer par rĂŽle dans un endpoint ```php #[Route('/api/admin-data')] #[IsGranted('ROLE_USER')] public function adminData(): JsonResponse { if (!$this->isGranted('ROLE_ADMIN')) { return $this->json( ['error' => 'AccĂšs admin requis'], JsonResponse::HTTP_FORBIDDEN ); } return $this->json([ 'adminData' => 'Secret data', ]); } ``` ### Valider les requĂȘtes ```php #[Route('/api/create-item', methods: ['POST'])] #[IsGranted('ROLE_USER')] public function createItem(Request $request): JsonResponse { $data = json_decode($request->getContent(), true); // Valider les paramĂštres requis if (empty($data['name'])) { return $this->json( ['error' => 'name is required'], JsonResponse::HTTP_BAD_REQUEST ); } // Traiter les donnĂ©es... return $this->json(['id' => 1, 'name' => $data['name']]); } ``` --- ## FAQ et dĂ©pannage ### Q: Erreur "Invalid or expired API token" **R:** Le token est invalide ou expirĂ©. - VĂ©rifier que le token est correct - CrĂ©er une nouvelle clĂ©: `php bin/console app:api-key:create` - VĂ©rifier l'expiration ### Q: Comment mettre Ă  jour ma clĂ©? **R:** Les clĂ©s ne peuvent pas ĂȘtre mises Ă  jour. Solutions: - CrĂ©er une nouvelle clĂ© avec une date d'expiration plus lointaine - RĂ©voquer l'ancienne clĂ© ### Q: Puis-je utiliser la mĂȘme clĂ© pour plusieurs applications? **R:** Techniquement oui, mais **dĂ©conseillĂ©** pour des raisons de sĂ©curitĂ©: - Une compromission affecte toutes les applications - Impossible de rĂ©voquer une seule application - CrĂ©er une clĂ© par application ### Q: Comment changer l'expiration d'une clĂ©? **R:** Ce n'est pas possible directement. Options: 1. CrĂ©er une nouvelle clĂ© avec expiration souhaitĂ©e 2. RĂ©voquer l'ancienne 3. Mettre Ă  jour l'application ### Q: L'API fonctionne-t-elle avec HTTPS? **R:** Oui, recommandĂ© en production. Assurez-vous que: - Les certificats sont valides - Les URLs sont en HTTPS - Les cookies ont le flag `Secure` ### Q: Puis-je accĂ©der Ă  l'API depuis un autre domaine (CORS)? **R:** CORS n'est pas configurĂ© par dĂ©faut. Pour l'activer: 1. Installer bundle CORS: `composer require nelmio/cors-bundle` 2. Configurer les domaines autorisĂ©s ### Q: Comment monitorer l'utilisation de l'API? **R:** VĂ©rifier: - `lastUsedAt` dans GET `/api/api-keys` - Les logs: `var/log/dev.log` - ImplĂ©menter un logging personnalisĂ© ### Q: Erreur "403 Forbidden" **R:** Vous n'avez pas les permissions. VĂ©rifier: - Votre rĂŽle (GET `/api/profile`) - Les droits requis (@IsGranted) - Si vous essayez de modifier les donnĂ©es d'un autre ### Q: Comment rĂ©initialiser toutes mes clĂ©s? **R:** 1. Lister les clĂ©s: `GET /api/api-keys` 2. Supprimer une par une: `DELETE /api/api-keys/{id}` 3. CrĂ©er de nouvelles clĂ©s --- **Documentation Ă  jour: 2026-04-03**