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 */}
+ 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.
+