Files
FamilyPlanner/docs/archive/CORRECTIONS_OAUTH.md
philippe fdd72c1135 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>
2025-10-14 10:43:33 +02:00

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_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) :

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 :

  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 :

// É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

  1. Clé OpenAI exposée dans Git :

  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é

// 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 :

    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.