Initial commit: Family Planner application

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>
This commit is contained in:
philippe
2025-10-14 10:43:33 +02:00
commit fdd72c1135
239 changed files with 44160 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
# 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`.