diff --git a/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts b/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts index b2cdc5d14f..dfe6f82f21 100644 --- a/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts +++ b/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts @@ -54,6 +54,23 @@ import { } from "@calcom/platform-constants"; import { ApiResponse, CalendarBusyTimesInput, CreateCalendarCredentialsInput } from "@calcom/platform-types"; +export interface CalendarState { + accessToken: string; + origin: string; + redir?: string; + isDryRun?: boolean; +} + +const calendarStateSchema = z.object({ + accessToken: z.string(), + origin: z.string(), + redir: z.string().optional(), + isDryRun: z + .string() + .optional() + .transform((val) => val === "true"), +}); + @Controller({ path: "/v2/calendars", version: API_VERSIONS_VALUES, @@ -181,32 +198,31 @@ export class CalendarsController { @Query("code") code: string, @Param("calendar") calendar: string ): Promise<{ url: string }> { - // state params contains our user access token - const stateParams = new URLSearchParams(state); - const { accessToken, origin, redir, isDryRun } = z - .object({ - accessToken: z.string(), - origin: z.string(), - redir: z.string().nullish().optional(), - isDryRun: z.string().nullish().optional(), - }) - .parse({ + let stateObj: CalendarState; + + try { + // First try to parse as JSON + stateObj = JSON.parse(state) as CalendarState; + } catch (e) { + // If JSON parsing fails, try URL params + const stateParams = new URLSearchParams(state); + + const parsedState = calendarStateSchema.parse({ accessToken: stateParams.get("accessToken"), origin: stateParams.get("origin"), - redir: stateParams.get("redir"), + redir: stateParams.get("redir") || undefined, isDryRun: stateParams.get("isDryRun"), }); + + stateObj = parsedState; + } + + const { accessToken, origin, redir, isDryRun } = stateObj; switch (calendar) { case OFFICE_365_CALENDAR: - return await this.outlookService.save(code, accessToken, origin, redir ?? "", isDryRun === "true"); + return await this.outlookService.save(code, accessToken, origin, redir ?? "", !!isDryRun); case GOOGLE_CALENDAR: - return await this.googleCalendarService.save( - code, - accessToken, - origin, - redir ?? "", - isDryRun === "true" - ); + return await this.googleCalendarService.save(code, accessToken, origin, redir ?? "", !!isDryRun); default: throw new BadRequestException( "Invalid calendar type, available calendars are: ", diff --git a/apps/api/v2/src/ee/calendars/services/gcal.service.ts b/apps/api/v2/src/ee/calendars/services/gcal.service.ts index f4273eeb90..38e1f11d6d 100644 --- a/apps/api/v2/src/ee/calendars/services/gcal.service.ts +++ b/apps/api/v2/src/ee/calendars/services/gcal.service.ts @@ -1,4 +1,5 @@ import { OAuthCalendarApp } from "@/ee/calendars/calendars.interface"; +import type { CalendarState } from "@/ee/calendars/controllers/calendars.controller"; import { CalendarsService } from "@/ee/calendars/services/calendars.service"; import { AppsRepository } from "@/modules/apps/apps.repository"; import { CredentialsRepository } from "@/modules/credentials/credentials.repository"; @@ -65,12 +66,18 @@ export class GoogleCalendarService implements OAuthCalendarApp { async getCalendarRedirectUrl(accessToken: string, origin: string, redir?: string, isDryRun?: boolean) { const oAuth2Client = await this.getOAuthClient(this.redirectUri); + const state: CalendarState = { + accessToken, + origin, + redir, + isDryRun, + }; const authUrl = oAuth2Client.generateAuthUrl({ access_type: "offline", scope: CALENDAR_SCOPES, prompt: "consent", - state: `accessToken=${accessToken}&origin=${origin}&redir=${redir ?? ""}&isDryRun=${isDryRun}`, + state: JSON.stringify(state), }); return authUrl; diff --git a/apps/api/v2/src/ee/calendars/services/outlook.service.ts b/apps/api/v2/src/ee/calendars/services/outlook.service.ts index 67c36fe836..8899cd9a1d 100644 --- a/apps/api/v2/src/ee/calendars/services/outlook.service.ts +++ b/apps/api/v2/src/ee/calendars/services/outlook.service.ts @@ -1,4 +1,5 @@ import { OAuthCalendarApp } from "@/ee/calendars/calendars.interface"; +import { CalendarState } from "@/ee/calendars/controllers/calendars.controller"; import { CalendarsService } from "@/ee/calendars/services/calendars.service"; import { CredentialsRepository } from "@/modules/credentials/credentials.repository"; import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository"; @@ -60,6 +61,13 @@ export class OutlookService implements OAuthCalendarApp { async getCalendarRedirectUrl(accessToken: string, origin: string, redir?: string, isDryRun?: boolean) { const { client_id } = await this.calendarsService.getAppKeys(OFFICE_365_CALENDAR_ID); + const state: CalendarState = { + accessToken, + origin, + redir, + isDryRun, + }; + const scopes = ["User.Read", "Calendars.Read", "Calendars.ReadWrite", "offline_access"]; const params = { response_type: "code", @@ -67,7 +75,7 @@ export class OutlookService implements OAuthCalendarApp { client_id, prompt: "select_account", redirect_uri: this.redirectUri, - state: `accessToken=${accessToken}&origin=${origin}&redir=${redir ?? ""}&isDryRun=${isDryRun}`, + state: JSON.stringify(state), }; const query = stringify(params);