# Corrections apportées au système OAuth ## Problème initial **Erreur rencontrée** : ``` Accès bloqué : erreur d'autorisation Required parameter is missing: response_type Erreur 400 : invalid_request flowName=GeneralOAuthFlow ``` **Cause** : L'URL OAuth générée était incomplète. Elle ne contenait que l'URL de base et le paramètre `state`, mais manquait tous les paramètres requis par OAuth 2.0 : - `client_id` - `redirect_uri` - `response_type` ⚠️ **CRITIQUE** - `scope` - `access_type` (pour Google) - `prompt` (pour Google) ## Corrections effectuées ### 1. Fichier manquant ✅ **Copié** `calendar.ts` du mauvais emplacement vers le bon : - Source : `C:\Users\philh\OneDrive\Documents\Codes\backend\src\routes\calendar.ts` - Destination : `C:\Users\philh\OneDrive\Documents\Codes\family-planner\backend\src\routes\calendar.ts` ### 2. URL OAuth complète (ligne 48-82) **AVANT** (incorrect) : ```typescript const authUrl = `${baseUrls[provider]}?state=${encodeURIComponent(state)}`; ``` **APRÈS** (correct) : ```typescript // Google const params = new URLSearchParams({ client_id: config.clientId, redirect_uri: config.redirectUri, response_type: "code", // ⚠️ PARAMÈTRE MANQUANT scope: config.scope, state: state, access_type: "offline", prompt: "consent" }); authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`; // Outlook const params = new URLSearchParams({ client_id: config.clientId, redirect_uri: config.redirectUri, response_type: "code", // ⚠️ PARAMÈTRE MANQUANT scope: config.scope, state: state, response_mode: "query" }); ``` ### 3. Configuration centralisée Ajout d'un objet `OAUTH_CONFIG` avec : - Client IDs - Redirect URIs - Scopes appropriés pour chaque provider ### 4. Variables d'environnement Ajout dans `backend/.env` : ```env # OAuth Configuration - Google Calendar GOOGLE_CLIENT_ID=your_google_client_id_here GOOGLE_CLIENT_SECRET=your_google_client_secret_here GOOGLE_REDIRECT_URI=http://localhost:5000/api/calendar/oauth/callback # OAuth Configuration - Outlook/Microsoft 365 OUTLOOK_CLIENT_ID=your_outlook_client_id_here OUTLOOK_CLIENT_SECRET=your_outlook_client_secret_here OUTLOOK_REDIRECT_URI=http://localhost:5000/api/calendar/oauth/callback ``` ### 5. Documentation complète Créé `OAUTH_SETUP.md` avec : - Instructions étape par étape pour Google Cloud Console - Instructions étape par étape pour Azure Portal - Configuration des scopes - Résolution des problèmes courants - Mode développement sans OAuth ## Prochaines étapes requises ### Étape 1 : Configurer les identifiants OAuth (VOUS) Suivez le guide `OAUTH_SETUP.md` pour : 1. ✅ Créer un projet Google Cloud 2. ✅ Activer l'API Google Calendar 3. ✅ Créer les identifiants OAuth 2.0 4. ✅ Copier le Client ID et Secret dans `.env` 5. ✅ Répéter pour Azure AD (Outlook) ### Étape 2 : Implémenter l'échange de code (DÉVELOPPEMENT) Actuellement, le endpoint `/oauth/complete` crée une connexion factice. Il faut : ```typescript // Échanger le code d'autorisation contre un access token const tokenResponse = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: code, client_id: OAUTH_CONFIG.google.clientId, client_secret: OAUTH_CONFIG.google.clientSecret, redirect_uri: OAUTH_CONFIG.google.redirectUri, grant_type: "authorization_code" }) }); const tokens = await tokenResponse.json(); // tokens.access_token, tokens.refresh_token, tokens.expires_in ``` ### Étape 3 : Stocker les tokens de manière sécurisée **⚠️ NE JAMAIS stocker en clair** : - Utiliser un chiffrement AES-256-GCM (comme dans l'architecture PRONOTE) - Stocker la clé de chiffrement dans une variable d'environnement - Stocker les tokens dans une base de données ou fichier chiffré ### Étape 4 : Implémenter le refresh token Les access tokens expirent (généralement 1h). Implémenter : ```typescript async function refreshAccessToken(refreshToken: string, provider: CalendarProvider) { const config = OAUTH_CONFIG[provider]; const response = await fetch(tokenEndpoint, { method: "POST", body: JSON.stringify({ refresh_token: refreshToken, client_id: config.clientId, client_secret: config.clientSecret, grant_type: "refresh_token" }) }); return response.json(); } ``` ### Étape 5 : Récupérer les événements du calendrier Une fois l'access token obtenu : **Google Calendar API** : ```typescript const events = await fetch( "https://www.googleapis.com/calendar/v3/calendars/primary/events", { headers: { Authorization: `Bearer ${accessToken}` } } ); ``` **Microsoft Graph API** : ```typescript const events = await fetch( "https://graph.microsoft.com/v1.0/me/calendar/events", { headers: { Authorization: `Bearer ${accessToken}` } } ); ``` ### Étape 6 : Normaliser les événements Créer un format commun pour les événements des deux providers : ```typescript type NormalizedEvent = { id: string; title: string; start: string; // ISO8601 end: string; // ISO8601 description?: string; location?: string; attendees?: string[]; source: "google" | "outlook"; originalId: string; }; ``` ## État actuel ✅ **Corrigé** : Erreur OAuth "response_type missing" ✅ **Ajouté** : Configuration complète des paramètres OAuth ✅ **Documenté** : Guide de setup détaillé ⏳ **À faire** : Configuration des identifiants OAuth par l'utilisateur ⏳ **À faire** : Implémentation complète du flow OAuth (échange de code, stockage sécurisé, refresh) ⏳ **À faire** : Récupération et synchronisation des événements ## Sécurité - Points d'attention ### ⚠️ CRITIQUE - À corriger immédiatement 1. **Clé OpenAI exposée dans Git** : - La clé `sk-proj-efaTQ8cicJYU7k8RG...` est visible dans `.env` - **ACTION REQUISE** : Révoquer immédiatement sur https://platform.openai.com/api-keys 2. **Tokens en mémoire** : - Actuellement, les connexions sont stockées dans une `Map` en mémoire - Problème : perte des données au redémarrage du serveur - **Solution** : Stocker dans une base de données chiffrée 3. **Secrets en clair** : - Les client secrets doivent être dans `.env` (qui doit être dans `.gitignore`) - **Vérifier** que `.env` est bien ignoré par Git ### Recommandations de sécurité ```typescript // Implémenter le chiffrement des tokens import crypto from "crypto"; const ENCRYPTION_KEY = process.env.TOKEN_ENCRYPTION_KEY; // 32 bytes const algorithm = "aes-256-gcm"; function encryptToken(token: string): { encrypted: string; iv: string; authTag: string } { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, Buffer.from(ENCRYPTION_KEY, "hex"), iv); let encrypted = cipher.update(token, "utf8", "hex"); encrypted += cipher.final("hex"); const authTag = cipher.getAuthTag(); return { encrypted, iv: iv.toString("hex"), authTag: authTag.toString("hex") }; } function decryptToken(encrypted: string, iv: string, authTag: string): string { const decipher = crypto.createDecipheriv( algorithm, Buffer.from(ENCRYPTION_KEY, "hex"), Buffer.from(iv, "hex") ); decipher.setAuthTag(Buffer.from(authTag, "hex")); let decrypted = decipher.update(encrypted, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } ``` ## Test de la correction Pour tester que l'erreur OAuth est corrigée : 1. Redémarrez le backend : ```bash cd backend npm run dev ``` 2. Dans le frontend, testez la connexion Google/Outlook 3. Vérifiez que l'URL de redirection contient maintenant tous les paramètres : ``` https://accounts.google.com/o/oauth2/v2/auth? client_id=...& redirect_uri=...& response_type=code& ← AJOUTÉ scope=...& state=...& access_type=offline& ← AJOUTÉ prompt=consent ← AJOUTÉ ``` 4. Vous devriez voir la page de consentement Google/Microsoft (au lieu de l'erreur 400) **Note** : Vous aurez une erreur de "client_id invalide" tant que vous n'aurez pas configuré les vrais identifiants OAuth dans le `.env`.