diff --git a/OAUTH_SETUP.md b/OAUTH_SETUP.md index 429154a..7c0fd37 100644 --- a/OAUTH_SETUP.md +++ b/OAUTH_SETUP.md @@ -1,6 +1,25 @@ -# Configuration OAuth Google Calendar +# Configuration Google Calendar -Ce guide vous aide à résoudre les erreurs d'authentification OAuth Google. +Ce guide vous aide à configurer l'accès à Google Calendar avec deux méthodes disponibles : **OAuth 2.0** et **Service Account**. + +## Choisir la bonne méthode + +### OAuth 2.0 (Recommandé pour usage personnel) +- ✅ Accès au calendrier personnel de l'utilisateur +- ✅ Sécurisé avec consentement explicite +- ✅ Choix du compte Google à la connexion +- ⚠️ Nécessite une interaction utilisateur (popup de consentement) + +### Service Account (Pour applications automatisées) +- ✅ Aucune interaction utilisateur requise +- ✅ Idéal pour calendriers partagés en entreprise +- ✅ Fonctionne en arrière-plan +- ⚠️ Nécessite de partager le calendrier avec le Service Account +- ⚠️ Configuration plus technique + +--- + +# Configuration OAuth 2.0 ## Problème : Erreur 400 - redirect_uri_mismatch @@ -86,10 +105,107 @@ Si vous ne voyez toujours pas le choix du compte : - [ ] Application redémarrée après modification du `.env` - [ ] Attendu 5-10 minutes après modification dans Google Cloud Console +--- + +# Configuration Service Account + +Le Service Account permet un accès automatisé sans interaction utilisateur, idéal pour des calendriers partagés. + +## Étapes de configuration + +### 1. Créer un Service Account + +1. Allez sur [Google Cloud Console - Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) + +2. Sélectionnez votre projet (ou créez-en un nouveau) + +3. Cliquez sur **"+ CREATE SERVICE ACCOUNT"** + +4. Donnez un nom au compte (ex: `noteflow-calendar`) + +5. Cliquez sur **"CREATE AND CONTINUE"** + +6. Sautez les étapes optionnelles et cliquez sur **"DONE"** + +### 2. Générer une clé JSON + +1. Dans la liste des Service Accounts, cliquez sur celui que vous venez de créer + +2. Allez dans l'onglet **"KEYS"** + +3. Cliquez sur **"ADD KEY"** > **"Create new key"** + +4. Choisissez le format **JSON** + +5. Téléchargez le fichier JSON (conservez-le en sécurité !) + +### 3. Activer l'API Google Calendar + +1. Allez sur [Google Cloud Console - API Library](https://console.cloud.google.com/apis/library) + +2. Recherchez **"Google Calendar API"** + +3. Cliquez sur **"ENABLE"** + +### 4. Partager votre calendrier + +1. Ouvrez [Google Calendar](https://calendar.google.com) + +2. Trouvez votre calendrier dans la liste à gauche + +3. Cliquez sur les **3 points** à côté du nom du calendrier + +4. Sélectionnez **"Paramètres et partage"** + +5. Faites défiler jusqu'à **"Partager avec des personnes en particulier"** + +6. Cliquez sur **"+ Ajouter des personnes"** + +7. Collez l'**email du Service Account** (trouvé dans le fichier JSON, champ `client_email`) + +8. Donnez les permissions **"Afficher tous les détails des événements"** + +9. Cliquez sur **"Envoyer"** + +### 5. Configurer NoteFlow + +1. Dans NoteFlow, allez dans **Administration** > **Google Calendar** + +2. Sélectionnez **"Service Account"** dans le menu déroulant + +3. Ouvrez le fichier JSON téléchargé et **copiez tout son contenu** + +4. Collez le contenu dans le champ **"Clé Service Account (JSON)"** + +5. Entrez votre email (celui du calendrier partagé) dans **"Email du calendrier à synchroniser"** + +6. Cliquez sur **"Enregistrer"** + +7. Cliquez sur **"Synchroniser"** pour tester + +## Dépannage Service Account + +### Erreur : "Clé Service Account invalide" +- Vérifiez que vous avez copié l'intégralité du fichier JSON +- Assurez-vous qu'il n'y a pas d'espaces ou de caractères en trop + +### Erreur : "Calendar not found" ou "Permission denied" +- Vérifiez que vous avez bien partagé le calendrier avec l'email du Service Account +- Vérifiez que l'email du calendrier est correct +- Attendez quelques minutes après avoir partagé le calendrier + +### Les événements ne se synchronisent pas +- Vérifiez que l'API Google Calendar est activée dans votre projet +- Vérifiez les logs de l'application pour plus de détails +- Assurez-vous que le Service Account a les bonnes permissions + +--- + ## Besoin d'aide ? Si le problème persiste : 1. Vérifiez les logs de l'application pour des messages d'erreur détaillés -2. Assurez-vous que votre navigateur accepte les cookies tiers -3. Essayez en navigation privée pour éliminer les problèmes de cache +2. Pour OAuth 2.0 : Assurez-vous que votre navigateur accepte les cookies tiers +3. Pour OAuth 2.0 : Essayez en navigation privée pour éliminer les problèmes de cache 4. Vérifiez que l'API Google Calendar est bien activée dans votre projet Google Cloud +5. Pour Service Account : Vérifiez que le calendrier est bien partagé avec le bon email diff --git a/README_GOOGLE_CALENDAR.md b/README_GOOGLE_CALENDAR.md new file mode 100644 index 0000000..6b7d352 --- /dev/null +++ b/README_GOOGLE_CALENDAR.md @@ -0,0 +1,68 @@ +# Google Calendar - Deux méthodes d'authentification + +NoteFlow supporte maintenant **deux méthodes** pour se connecter à Google Calendar : + +## 🔐 OAuth 2.0 (Recommandé pour usage personnel) +**Avantages** : +- Accès sécurisé à votre calendrier personnel +- Choix du compte Google lors de la connexion +- Consentement explicite de l'utilisateur + +**Utilisation** : Administration > Google Calendar > Méthode : OAuth 2.0 + +[📖 Guide complet OAuth 2.0](./OAUTH_SETUP.md#configuration-oauth-20) + +--- + +## 🤖 Service Account (Pour automatisation) +**Avantages** : +- Aucune interaction utilisateur requise +- Parfait pour calendriers partagés en entreprise +- Fonctionne en arrière-plan automatiquement + +**Utilisation** : Administration > Google Calendar > Méthode : Service Account + +[📖 Guide complet Service Account](./OAUTH_SETUP.md#configuration-service-account) + +--- + +## Problèmes courants résolus + +### ✅ Erreur 400 : redirect_uri_mismatch +- Variable `APP_URL` ajoutée dans `.env.example` +- Guide de configuration détaillé dans OAUTH_SETUP.md + +### ✅ Pas de choix de compte Google +- Le prompt `select_account` force maintenant l'affichage du sélecteur + +### ✅ Besoin d'accès automatisé sans popup +- Utilisez Service Account pour une authentification sans interaction + +--- + +## Configuration rapide + +### Pour OAuth 2.0 : +1. Créez `.env` avec `APP_URL=http://localhost:2222` +2. Configurez OAuth 2.0 dans Google Cloud Console +3. Dans NoteFlow : Administration > Google Calendar > OAuth 2.0 +4. Ajoutez Client ID et Client Secret +5. Cliquez sur "Se connecter avec Google" + +### Pour Service Account : +1. Créez un Service Account sur Google Cloud Console +2. Téléchargez la clé JSON +3. Partagez votre calendrier avec l'email du Service Account +4. Dans NoteFlow : Administration > Google Calendar > Service Account +5. Collez le JSON et l'email du calendrier +6. Cliquez sur "Synchroniser" + +--- + +## Documentation complète + +📚 Consultez [OAUTH_SETUP.md](./OAUTH_SETUP.md) pour : +- Guide pas à pas complet +- Résolution des erreurs +- Checklist de vérification +- Conseils de dépannage diff --git a/routes/calendar.routes.js b/routes/calendar.routes.js index 4956ec8..e9e1d63 100644 --- a/routes/calendar.routes.js +++ b/routes/calendar.routes.js @@ -12,6 +12,22 @@ router.use(authenticateToken); // Scopes Google Calendar requis const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']; +/** + * Types d'authentification supportés + */ +const AUTH_TYPES = { + OAUTH2: 'oauth2', + SERVICE_ACCOUNT: 'service_account' +}; + +/** + * Récupérer le type d'authentification configuré + */ +async function getAuthType() { + const authType = await getOne("SELECT value FROM settings WHERE key = 'google_auth_type'"); + return authType?.value || AUTH_TYPES.OAUTH2; // Par défaut OAuth2 +} + /** * Créer un client OAuth2 * @param {string} [customRedirectUri] - URI de redirection personnalisée (optionnel) @@ -35,6 +51,51 @@ async function getOAuth2Client(customRedirectUri = null) { ); } +/** + * Créer un client avec Service Account + */ +async function getServiceAccountClient() { + const serviceAccountKey = await getOne("SELECT value FROM settings WHERE key = 'google_service_account_key'"); + + if (!serviceAccountKey || !serviceAccountKey.value) { + throw new Error('Clé Service Account non configurée'); + } + + try { + const credentials = JSON.parse(serviceAccountKey.value); + + const auth = new google.auth.GoogleAuth({ + credentials, + scopes: SCOPES + }); + + return await auth.getClient(); + } catch (error) { + logger.error('Erreur lors de la création du client Service Account:', error); + throw new Error('Clé Service Account invalide'); + } +} + +/** + * Créer un client Google Calendar selon la méthode d'authentification + * @param {number} userId - ID de l'utilisateur (pour OAuth2) + */ +async function getCalendarClient(userId = null) { + const authType = await getAuthType(); + + if (authType === AUTH_TYPES.SERVICE_ACCOUNT) { + const auth = await getServiceAccountClient(); + return google.calendar({ version: 'v3', auth }); + } else { + // OAuth2 + if (!userId) { + throw new Error('User ID requis pour OAuth2'); + } + const oauth2Client = await getValidTokens(userId); + return google.calendar({ version: 'v3', auth: oauth2Client }); + } +} + /** * GET /api/calendar/auth-url * Générer l'URL d'authentification OAuth @@ -153,19 +214,36 @@ router.get('/oauth-callback', async (req, res) => { */ router.get('/auth-status', async (req, res) => { try { - const tokens = await getOne( - 'SELECT access_token, expiry_date FROM google_oauth_tokens WHERE user_id = ?', - [req.user.id] - ); + const authType = await getAuthType(); - const isAuthenticated = !!tokens; - const isExpired = tokens && tokens.expiry_date && tokens.expiry_date < Date.now(); + if (authType === AUTH_TYPES.SERVICE_ACCOUNT) { + // Vérifier si la clé Service Account est configurée + const serviceAccountKey = await getOne("SELECT value FROM settings WHERE key = 'google_service_account_key'"); + const isAuthenticated = !!serviceAccountKey?.value; - res.json({ - isAuthenticated, - isExpired, - needsReauth: !isAuthenticated || isExpired - }); + res.json({ + authType: AUTH_TYPES.SERVICE_ACCOUNT, + isAuthenticated, + isExpired: false, + needsReauth: !isAuthenticated + }); + } else { + // OAuth2 + const tokens = await getOne( + 'SELECT access_token, expiry_date FROM google_oauth_tokens WHERE user_id = ?', + [req.user.id] + ); + + const isAuthenticated = !!tokens; + const isExpired = tokens && tokens.expiry_date && tokens.expiry_date < Date.now(); + + res.json({ + authType: AUTH_TYPES.OAUTH2, + isAuthenticated, + isExpired, + needsReauth: !isAuthenticated || isExpired + }); + } } catch (error) { logger.error('Erreur lors de la vérification du statut OAuth:', error); res.status(500).json({ error: 'Erreur serveur' }); @@ -246,15 +324,23 @@ router.get('/events', async (req, res) => { /** * POST /api/calendar/sync - * Synchroniser avec Google Calendar via OAuth + * Synchroniser avec Google Calendar (OAuth2 ou Service Account) */ router.post('/sync', async (req, res) => { try { - // Obtenir un client OAuth valide - const oauth2Client = await getValidTokens(req.user.id); + const authType = await getAuthType(); + let calendarId = 'primary'; // Par défaut : calendrier principal - // Créer un client Google Calendar avec OAuth - const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); + // Si Service Account, permettre de spécifier l'email du calendrier + if (authType === AUTH_TYPES.SERVICE_ACCOUNT) { + const calendarEmail = await getOne("SELECT value FROM settings WHERE key = 'google_calendar_email'"); + if (calendarEmail?.value) { + calendarId = calendarEmail.value; + } + } + + // Créer un client Google Calendar selon la méthode d'authentification + const calendar = await getCalendarClient(req.user.id); // Récupérer les événements des 30 prochains jours const now = new Date(); @@ -262,7 +348,7 @@ router.post('/sync', async (req, res) => { futureDate.setDate(futureDate.getDate() + 30); const response = await calendar.events.list({ - calendarId: 'primary', // Calendrier principal de l'utilisateur + calendarId, // Utilise le calendrier configuré timeMin: now.toISOString(), timeMax: futureDate.toISOString(), maxResults: 50, diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 7b894b7..b8afb7b 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1345,52 +1345,129 @@ const Index = () => { - Configuration Google Calendar OAuth 2.0 + Configuration Google Calendar + {/* Choix du type d'authentification */}
- - setSettings({ ...settings, google_client_id: e.target.value })} - /> + + +

+ OAuth 2.0 : Pour accéder au calendrier personnel avec autorisation de l'utilisateur.
+ Service Account : Pour accéder à des calendriers partagés sans interaction utilisateur. +

-
- - setSettings({ ...settings, google_client_secret: e.target.value })} - /> -

- Créez un projet OAuth 2.0 sur{' '} - - Google Cloud Console - - {' '}et activez l'API Google Calendar. -

-
-

- ⚠️ IMPORTANT : URI de redirection autorisée -

-

- Ajoutez cette URL exacte dans la console Google Cloud (OAuth 2.0 Client IDs → URIs de redirection autorisées) : -

- - {window.location.origin}/api/calendar/oauth-callback - -

- 💡 Si vous utilisez un domaine différent, créez un fichier .env avec APP_URL=https://votre-domaine.com -

-
-
+ {/* Configuration OAuth2 */} + {(!settings.google_auth_type || settings.google_auth_type === 'oauth2') && ( + <> +
+ + setSettings({ ...settings, google_client_id: e.target.value })} + /> +
+ +
+ + setSettings({ ...settings, google_client_secret: e.target.value })} + /> +

+ Créez un projet OAuth 2.0 sur{' '} + + Google Cloud Console + + {' '}et activez l'API Google Calendar. +

+
+

+ ⚠️ IMPORTANT : URI de redirection autorisée +

+

+ Ajoutez cette URL exacte dans la console Google Cloud (OAuth 2.0 Client IDs → URIs de redirection autorisées) : +

+ + {window.location.origin}/api/calendar/oauth-callback + +

+ 💡 Si vous utilisez un domaine différent, créez un fichier .env avec APP_URL=https://votre-domaine.com +

+
+
+ + )} + + {/* Configuration Service Account */} + {settings.google_auth_type === 'service_account' && ( + <> +
+ +