Complete family planning application with: - React frontend with TypeScript - Node.js/Express backend with TypeScript - Python ingestion service for document processing - Planning ingestion service with LLM integration - Shared UI components and type definitions - OAuth integration for calendar synchronization - Comprehensive documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8.1 KiB
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_idredirect_uriresponse_type⚠️ CRITIQUEscopeaccess_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) :
const authUrl = `${baseUrls[provider]}?state=${encodeURIComponent(state)}`;
APRÈS (correct) :
// 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 :
# 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 :
- ✅ Créer un projet Google Cloud
- ✅ Activer l'API Google Calendar
- ✅ Créer les identifiants OAuth 2.0
- ✅ Copier le Client ID et Secret dans
.env - ✅ 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 :
// É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 :
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 :
const events = await fetch(
"https://www.googleapis.com/calendar/v3/calendars/primary/events",
{
headers: {
Authorization: `Bearer ${accessToken}`
}
}
);
Microsoft Graph API :
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 :
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
-
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
- La clé
-
Tokens en mémoire :
- Actuellement, les connexions sont stockées dans une
Mapen mémoire - Problème : perte des données au redémarrage du serveur
- Solution : Stocker dans une base de données chiffrée
- Actuellement, les connexions sont stockées dans une
-
Secrets en clair :
- Les client secrets doivent être dans
.env(qui doit être dans.gitignore) - Vérifier que
.envest bien ignoré par Git
- Les client secrets doivent être dans
Recommandations de sécurité
// 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 :
-
Redémarrez le backend :
cd backend npm run dev -
Dans le frontend, testez la connexion Google/Outlook
-
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É -
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.