diff --git a/app/agenda/page.tsx b/app/agenda/page.tsx
index 483c7e4..55011c0 100644
--- a/app/agenda/page.tsx
+++ b/app/agenda/page.tsx
@@ -273,10 +273,11 @@ export default async function CalendarPage() {
}
}
} catch (error) {
- console.error(`Error auto-setting up sync for Microsoft account ${account.email}:`, error);
- // Don't fail the page if Microsoft sync setup fails
- // The account might not have the calendar scope yet, or there might be a token issue
- // User can manually set up sync later if needed
+ // Microsoft sync setup failed - likely because account doesn't have calendar scope yet
+ // This is expected for accounts authenticated before calendar scope was added
+ // User will need to re-authenticate their Microsoft account to get calendar access
+ console.log(`Microsoft calendar sync not available for ${account.email} - account may need re-authentication with calendar permissions`);
+ // Don't fail the page - continue with other accounts
}
}
}
diff --git a/lib/services/caldav-sync.ts b/lib/services/caldav-sync.ts
index 867ee94..27708e3 100644
--- a/lib/services/caldav-sync.ts
+++ b/lib/services/caldav-sync.ts
@@ -47,92 +47,52 @@ export async function discoverInfomaniakCalendars(
try {
const client = await getInfomaniakCalDAVClient(email, password);
- // Try to discover calendar home set using CalDAV discovery
- // First, try to find the calendar home set using current-user-principal
- let calendarHomePath = '/calendars/';
+ // List all calendars using PROPFIND on root
+ const items = await client.getDirectoryContents('/');
- // Extract username from email (before @) or use email as username
- // Infomaniak might use the email as username or require the Infomaniak username
- const username = email.split('@')[0];
+ const calendars: CalDAVCalendar[] = [];
- // Try different paths
- const possiblePaths = [
- `/calendars/${username}/`,
- `/calendars/${email}/`,
- '/calendars/',
- '/',
- ];
-
- let calendars: CalDAVCalendar[] = [];
-
- for (const path of possiblePaths) {
- try {
- // List all calendars using PROPFIND with Depth: 1
- const items = await client.getDirectoryContents(path);
-
- for (const item of items) {
- if (item.type === 'directory' && item.filename !== '/' && item.filename !== path) {
- // Get calendar properties
- try {
- const props = await client.customRequest(item.filename, {
- method: 'PROPFIND',
- headers: {
- Depth: '0',
- 'Content-Type': 'application/xml',
- },
- data: `
+ for (const item of items) {
+ if (item.type === 'directory' && item.filename !== '/') {
+ // Get calendar properties
+ try {
+ const props = await client.customRequest(item.filename, {
+ method: 'PROPFIND',
+ headers: {
+ Depth: '0',
+ 'Content-Type': 'application/xml',
+ },
+ data: `
-
`,
- });
+ });
- // Check if it's a calendar (has calendar resource type)
- const isCalendar = typeof props.data === 'string' &&
- (props.data.includes(' 0) {
- break;
- }
- } catch (pathError) {
- // Try next path
- logger.debug(`Path ${path} failed, trying next`, {
- error: pathError instanceof Error ? pathError.message : String(pathError),
- });
- continue;
}
}
diff --git a/lib/services/microsoft-calendar-sync.ts b/lib/services/microsoft-calendar-sync.ts
index 42183b8..7b4c7e1 100644
--- a/lib/services/microsoft-calendar-sync.ts
+++ b/lib/services/microsoft-calendar-sync.ts
@@ -39,10 +39,12 @@ async function getMicrosoftGraphClient(
email: string
): Promise {
// Ensure we have a fresh access token
+ // Note: The token might not have calendar scope if the account was authenticated before calendar scope was added
+ // In that case, the user will need to re-authenticate
const { accessToken, success } = await ensureFreshToken(userId, email);
if (!success || !accessToken) {
- throw new Error('Failed to obtain valid Microsoft access token');
+ throw new Error('Failed to obtain valid Microsoft access token. The account may need to be re-authenticated with calendar permissions.');
}
return accessToken;
@@ -84,21 +86,31 @@ export async function discoverMicrosoftCalendars(
return calendars;
} catch (error: any) {
- // Check if error is due to missing calendar scope
+ // Check if error is due to missing calendar scope or invalid audience
if (error.response?.status === 403 || error.response?.status === 401) {
- logger.warn('Microsoft calendar access denied - may need to re-authenticate with calendar scope', {
- userId,
- email,
- error: error.response?.data?.error?.message || error.message,
- });
- // Return empty array instead of throwing - user can re-authenticate later
- return [];
+ const errorMessage = error.response?.data?.error?.message || error.message || '';
+ const needsReauth = errorMessage.includes('Invalid audience') ||
+ errorMessage.includes('insufficient_privileges') ||
+ errorMessage.includes('invalid_token');
+
+ if (needsReauth) {
+ logger.warn('Microsoft calendar access denied - account needs re-authentication with calendar scope', {
+ userId,
+ email,
+ error: errorMessage,
+ });
+ // Return empty array - user needs to re-authenticate their Microsoft account
+ // The account was authenticated before calendar scope was added
+ return [];
+ }
}
logger.error('Error discovering Microsoft calendars', {
userId,
email,
error: error instanceof Error ? error.message : String(error),
+ responseStatus: error.response?.status,
+ responseData: error.response?.data,
});
// Return empty array instead of throwing to avoid breaking the page
return [];