diff --git a/packages/app-store/_utils/useAddAppMutation.ts b/packages/app-store/_utils/useAddAppMutation.ts index 76becbbf10..ee27f1d640 100644 --- a/packages/app-store/_utils/useAddAppMutation.ts +++ b/packages/app-store/_utils/useAddAppMutation.ts @@ -57,6 +57,7 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta { variant: variables && variables.variant, slug: variables && variables.slug }, location.search ), + fromApp: true, ...(type === "google_calendar" && { installGoogleVideo: options?.installGoogleVideo }), ...(teamId && { teamId }), }; diff --git a/packages/app-store/googlecalendar/api/add.ts b/packages/app-store/googlecalendar/api/add.ts index 7ed6fcf02d..6d2c65385a 100644 --- a/packages/app-store/googlecalendar/api/add.ts +++ b/packages/app-store/googlecalendar/api/add.ts @@ -8,7 +8,7 @@ import { defaultHandler, defaultResponder } from "@calcom/lib/server"; import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; import { encodeOAuthState } from "../../_utils/oauth/encodeOAuthState"; -const scopes = [ +export const scopes = [ "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events", ]; diff --git a/packages/app-store/googlecalendar/api/callback.ts b/packages/app-store/googlecalendar/api/callback.ts index 3577e9b092..16016d0a8f 100644 --- a/packages/app-store/googlecalendar/api/callback.ts +++ b/packages/app-store/googlecalendar/api/callback.ts @@ -10,6 +10,7 @@ import prisma from "@calcom/prisma"; import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; import getInstalledAppPath from "../../_utils/getInstalledAppPath"; import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState"; +import { scopes } from "./add"; let client_id = ""; let client_secret = ""; @@ -37,20 +38,44 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) { const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri); let key = ""; + let invalid = false; if (code) { const token = await oAuth2Client.getToken(code); key = token.res?.data; + // Check that the has granted all permissions + const grantedScopes = key.scope; + for (const scope of scopes) { + if (!grantedScopes.includes(scope)) { + if (!state?.fromApp) { + throw new HttpError({ + statusCode: 400, + message: "You must grant all permissions to use this integration", + }); + } else { + invalid = true; + } + } + } + const credential = await prisma.credential.create({ data: { type: "google_calendar", key, userId: req.session.user.id, appId: "google-calendar", + invalid, }, }); + if (invalid) { + res.redirect( + getSafeRedirectUrl(state?.returnTo) ?? + getInstalledAppPath({ variant: "calendar", slug: "google-calendar" }) + ); + } + // Set the primary calendar as the first selected calendar // We can ignore this type error because we just validated the key when we init oAuth2Client diff --git a/packages/app-store/types.d.ts b/packages/app-store/types.d.ts index b2f81b08c6..58025f0439 100644 --- a/packages/app-store/types.d.ts +++ b/packages/app-store/types.d.ts @@ -8,6 +8,7 @@ export type IntegrationOAuthCallbackState = { returnTo: string; installGoogleVideo?: boolean; teamId?: number; + fromApp?: boolean; }; export type CredentialOwner = {