Skip to content

Commit 8e7ce12

Browse files
authored
Revert "feat: install gcal + gvideo by default for google signups (#17773)" (#17803)
This reverts commit fcd06c8.
1 parent 4ddbdde commit 8e7ce12

File tree

13 files changed

+170
-249
lines changed

13 files changed

+170
-249
lines changed

Diff for: .env.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ NEXT_PUBLIC_FRESHCHAT_HOST=
119119

120120
# Google OAuth credentials
121121
# To enable Login with Google you need to:
122-
# 1. Set `GOOGLE_API_CREDENTIALS` below
122+
# 1. Set `GOOGLE_API_CREDENTIALS` above
123123
# 2. Set `GOOGLE_LOGIN_ENABLED` to `true`
124124
# When self-hosting please ensure you configure the Google integration as an Internal app so no one else can login to your instance
125125
# @see https://support.google.com/cloud/answer/6158849#public-and-internal&zippy=%2Cpublic-and-internal-applications

Diff for: packages/app-store/googlecalendar/api/add.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { google } from "googleapis";
22
import type { NextApiRequest, NextApiResponse } from "next";
33

4-
import { GOOGLE_CALENDAR_SCOPES, SCOPE_USERINFO_PROFILE, WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants";
4+
import { WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants";
55
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
66

77
import { encodeOAuthState } from "../../_utils/oauth/encodeOAuthState";
8+
import { SCOPES } from "../lib/constants";
89
import { getGoogleAppKeys } from "../lib/getGoogleAppKeys";
910

1011
async function getHandler(req: NextApiRequest, res: NextApiResponse) {
@@ -15,7 +16,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
1516

1617
const authUrl = oAuth2Client.generateAuthUrl({
1718
access_type: "offline",
18-
scope: [SCOPE_USERINFO_PROFILE, ...GOOGLE_CALENDAR_SCOPES],
19+
scope: SCOPES,
1920
// A refresh token is only returned the first time the user
2021
// consents to providing access. For illustration purposes,
2122
// setting the prompt to 'consent' will force this consent

Diff for: packages/app-store/googlecalendar/api/callback.ts

+21-22
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@ import { google } from "googleapis";
22
import type { NextApiRequest, NextApiResponse } from "next";
33

44
import { renewSelectedCalendarCredentialId } from "@calcom/lib/connectedCalendar";
5-
import {
6-
GOOGLE_CALENDAR_SCOPES,
7-
SCOPE_USERINFO_PROFILE,
8-
WEBAPP_URL,
9-
WEBAPP_URL_FOR_OAUTH,
10-
} from "@calcom/lib/constants";
5+
import { WEBAPP_URL, WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants";
116
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
7+
import { getAllCalendars, updateProfilePhoto } from "@calcom/lib/google";
128
import { HttpError } from "@calcom/lib/http-error";
139
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
1410
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
15-
import { GoogleService } from "@calcom/lib/server/service/google";
11+
import { GoogleRepository } from "@calcom/lib/server/repository/google";
1612
import { Prisma } from "@calcom/prisma/client";
1713

1814
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
1915
import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState";
16+
import { REQUIRED_SCOPES, SCOPE_USERINFO_PROFILE } from "../lib/constants";
2017
import { getGoogleAppKeys } from "../lib/getGoogleAppKeys";
2118

2219
async function getHandler(req: NextApiRequest, res: NextApiResponse) {
@@ -50,7 +47,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
5047
const key = token.tokens;
5148
const grantedScopes = token.tokens.scope?.split(" ") ?? [];
5249
// Check if we have granted all required permissions
53-
const hasMissingRequiredScopes = GOOGLE_CALENDAR_SCOPES.some((scope) => !grantedScopes.includes(scope));
50+
const hasMissingRequiredScopes = REQUIRED_SCOPES.some((scope) => !grantedScopes.includes(scope));
5451
if (hasMissingRequiredScopes) {
5552
if (!state?.fromApp) {
5653
throw new HttpError({
@@ -75,7 +72,19 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
7572
auth: oAuth2Client,
7673
});
7774

78-
const primaryCal = await GoogleService.getPrimaryCalendar(calendar);
75+
const cals = await getAllCalendars(calendar);
76+
77+
const primaryCal = cals.find((cal) => cal.primary) ?? cals[0];
78+
79+
// Only attempt to update the user's profile photo if the user has granted the required scope
80+
if (grantedScopes.includes(SCOPE_USERINFO_PROFILE)) {
81+
await updateProfilePhoto(oAuth2Client, req.session.user.id);
82+
}
83+
84+
const gcalCredential = await GoogleRepository.createGoogleCalendarCredential({
85+
key,
86+
userId: req.session.user.id,
87+
});
7988

8089
// If we still don't have a primary calendar skip creating the selected calendar.
8190
// It can be toggled on later.
@@ -87,16 +96,6 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
8796
return;
8897
}
8998

90-
// Only attempt to update the user's profile photo if the user has granted the required scope
91-
if (grantedScopes.includes(SCOPE_USERINFO_PROFILE)) {
92-
await GoogleService.updateProfilePhoto(oAuth2Client, req.session.user.id);
93-
}
94-
95-
const gcalCredential = await GoogleService.createGoogleCalendarCredential({
96-
key,
97-
userId: req.session.user.id,
98-
});
99-
10099
const selectedCalendarWhereUnique = {
101100
userId: req.session.user.id,
102101
externalId: primaryCal.id,
@@ -106,7 +105,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
106105
// Wrapping in a try/catch to reduce chance of race conditions-
107106
// also this improves performance for most of the happy-paths.
108107
try {
109-
await GoogleService.upsertSelectedCalendar({
108+
await GoogleRepository.upsertSelectedCalendar({
110109
credentialId: gcalCredential.id,
111110
externalId: selectedCalendarWhereUnique.externalId,
112111
userId: selectedCalendarWhereUnique.userId,
@@ -146,7 +145,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
146145
return;
147146
}
148147

149-
const existingGoogleMeetCredential = await GoogleService.findGoogleMeetCredential({
148+
const existingGoogleMeetCredential = await GoogleRepository.findGoogleMeetCredential({
150149
userId: req.session.user.id,
151150
});
152151

@@ -160,7 +159,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
160159
}
161160

162161
// Create a new google meet credential
163-
await GoogleService.createGoogleMeetsCredential({ userId: req.session.user.id });
162+
await GoogleRepository.createGoogleMeetsCredential({ userId: req.session.user.id });
164163
res.redirect(
165164
getSafeRedirectUrl(`${WEBAPP_URL}/apps/installed/conferencing?hl=google-meet`) ??
166165
getInstalledAppPath({ variant: "conferencing", slug: "google-meet" })

Diff for: packages/app-store/googlecalendar/lib/CalendarService.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import {
1818
CREDENTIAL_SYNC_SECRET_HEADER_NAME,
1919
} from "@calcom/lib/constants";
2020
import { formatCalEvent } from "@calcom/lib/formatCalendarEvent";
21+
import { getAllCalendars } from "@calcom/lib/google";
2122
import logger from "@calcom/lib/logger";
2223
import { safeStringify } from "@calcom/lib/safeStringify";
23-
import { GoogleService } from "@calcom/lib/server/service/google";
24+
import { GoogleRepository } from "@calcom/lib/server/repository/google";
2425
import prisma from "@calcom/prisma";
2526
import type {
2627
Calendar,
@@ -542,7 +543,7 @@ export default class GoogleCalendarService implements Calendar {
542543
}
543544
async function getCalIds() {
544545
if (selectedCalendarIds.length !== 0) return selectedCalendarIds;
545-
const cals = await GoogleService.getAllCalendars(calendar, ["id"]);
546+
const cals = await getAllCalendars(calendar, ["id"]);
546547
if (!cals.length) return [];
547548
return cals.reduce((c, cal) => (cal.id ? [...c, cal.id] : c), [] as string[]);
548549
}
@@ -606,7 +607,7 @@ export default class GoogleCalendarService implements Calendar {
606607
status: 200,
607608
statusText: "OK",
608609
data: {
609-
items: await GoogleService.getAllCalendars(calendar),
610+
items: await getAllCalendars(calendar),
610611
},
611612
})
612613
);
@@ -651,7 +652,7 @@ export default class GoogleCalendarService implements Calendar {
651652
},
652653
});
653654
const response = res.data;
654-
await GoogleService.upsertSelectedCalendar({
655+
await GoogleRepository.upsertSelectedCalendar({
655656
userId: this.credential.userId!,
656657
externalId: calendarId,
657658
credentialId: this.credential.id,
@@ -685,7 +686,7 @@ export default class GoogleCalendarService implements Calendar {
685686
.catch((err) => {
686687
console.warn(JSON.stringify(err));
687688
});
688-
await GoogleService.upsertSelectedCalendar({
689+
await GoogleRepository.upsertSelectedCalendar({
689690
userId: this.credential.userId!,
690691
externalId: calendarId,
691692
credentialId: this.credential.id,

Diff for: packages/app-store/googlecalendar/lib/constants.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const SCOPE_USERINFO_PROFILE = "https://www.googleapis.com/auth/userinfo.profile";
2+
export const SCOPE_CALENDAR_READONLY = "https://www.googleapis.com/auth/calendar.readonly";
3+
export const SCOPE_CALENDAR_EVENT = "https://www.googleapis.com/auth/calendar.events";
4+
5+
export const REQUIRED_SCOPES = [SCOPE_CALENDAR_READONLY, SCOPE_CALENDAR_EVENT];
6+
7+
export const SCOPES = [...REQUIRED_SCOPES, SCOPE_USERINFO_PROFILE];

Diff for: packages/app-store/googlecalendar/lib/utils.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { calendar_v3 } from "googleapis";
2+
3+
import logger from "@calcom/lib/logger";
4+
5+
export async function getAllCalendars(
6+
calendar: calendar_v3.Calendar,
7+
fields: string[] = ["id", "summary", "primary", "accessRole"]
8+
): Promise<calendar_v3.Schema$CalendarListEntry[]> {
9+
let allCalendars: calendar_v3.Schema$CalendarListEntry[] = [];
10+
let pageToken: string | undefined;
11+
12+
try {
13+
do {
14+
const response: any = await calendar.calendarList.list({
15+
fields: `items(${fields.join(",")}),nextPageToken`,
16+
pageToken,
17+
maxResults: 250, // 250 is max
18+
});
19+
20+
allCalendars = [...allCalendars, ...(response.data.items ?? [])];
21+
pageToken = response.data.nextPageToken;
22+
} while (pageToken);
23+
24+
return allCalendars;
25+
} catch (error) {
26+
logger.error("Error fetching all Google Calendars", { error });
27+
throw error;
28+
}
29+
}

Diff for: packages/features/auth/lib/next-auth-options.ts

+2-64
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Membership, Team, UserPermissionRole } from "@prisma/client";
22
import { waitUntil } from "@vercel/functions";
3-
import { google } from "googleapis";
43
import type { AuthOptions, Session, User } from "next-auth";
54
import type { JWT } from "next-auth/jwt";
65
import { encode } from "next-auth/jwt";
@@ -16,12 +15,7 @@ import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/Imperso
1615
import { getOrgFullOrigin, subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
1716
import { clientSecretVerifier, hostedCal, isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
1817
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
19-
import {
20-
GOOGLE_CALENDAR_SCOPES,
21-
GOOGLE_OAUTH_SCOPES,
22-
HOSTED_CAL_FEATURES,
23-
IS_CALCOM,
24-
} from "@calcom/lib/constants";
18+
import { HOSTED_CAL_FEATURES, IS_CALCOM } from "@calcom/lib/constants";
2519
import { ENABLE_PROFILE_SWITCHER, IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
2620
import { symmetricDecrypt, symmetricEncrypt } from "@calcom/lib/crypto";
2721
import { defaultCookies } from "@calcom/lib/default-cookies";
@@ -31,7 +25,6 @@ import { randomString } from "@calcom/lib/random";
3125
import { safeStringify } from "@calcom/lib/safeStringify";
3226
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
3327
import { UserRepository } from "@calcom/lib/server/repository/user";
34-
import { GoogleService } from "@calcom/lib/server/service/google";
3528
import slugify from "@calcom/lib/slugify";
3629
import prisma from "@calcom/prisma";
3730
import { IdentityProvider, MembershipRole } from "@calcom/prisma/enums";
@@ -258,13 +251,6 @@ if (IS_GOOGLE_LOGIN_ENABLED) {
258251
clientId: GOOGLE_CLIENT_ID,
259252
clientSecret: GOOGLE_CLIENT_SECRET,
260253
allowDangerousEmailAccountLinking: true,
261-
authorization: {
262-
params: {
263-
scope: [...GOOGLE_OAUTH_SCOPES, ...GOOGLE_CALENDAR_SCOPES].join(" "),
264-
access_type: "offline",
265-
prompt: "consent",
266-
},
267-
},
268254
})
269255
);
270256
}
@@ -622,55 +608,6 @@ export const getOptions = ({
622608
return await autoMergeIdentities();
623609
}
624610

625-
const grantedScopes = account.scope?.split(" ") ?? [];
626-
if (
627-
account.provider === "google" &&
628-
!(await GoogleService.findFirstGoogleCalendarCredential({
629-
userId: user.id as number,
630-
})) &&
631-
GOOGLE_CALENDAR_SCOPES.every((scope) => grantedScopes.includes(scope))
632-
) {
633-
// Installing Google Calendar by default
634-
const credentialkey = {
635-
access_token: account.access_token,
636-
refresh_token: account.refresh_token,
637-
id_token: account.id_token,
638-
token_type: account.token_type,
639-
expires_at: account.expires_at,
640-
};
641-
642-
const gcalCredential = await GoogleService.createGoogleCalendarCredential({
643-
userId: user.id as number,
644-
key: credentialkey,
645-
});
646-
647-
if (
648-
!(await GoogleService.findGoogleMeetCredential({
649-
userId: user.id as number,
650-
}))
651-
) {
652-
await GoogleService.createGoogleMeetsCredential({
653-
userId: user.id as number,
654-
});
655-
}
656-
657-
const oAuth2Client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET);
658-
oAuth2Client.setCredentials(credentialkey);
659-
const calendar = google.calendar({
660-
version: "v3",
661-
auth: oAuth2Client,
662-
});
663-
const primaryCal = await GoogleService.getPrimaryCalendar(calendar);
664-
if (primaryCal?.id) {
665-
await GoogleService.createSelectedCalendar({
666-
credentialId: gcalCredential.id,
667-
userId: user.id as number,
668-
externalId: primaryCal.id,
669-
});
670-
}
671-
await GoogleService.updateProfilePhoto(oAuth2Client, user.id as number);
672-
}
673-
674611
return {
675612
...token,
676613
id: existingUser.id,
@@ -695,6 +632,7 @@ export const getOptions = ({
695632
log.debug("callbacks:session - Session callback called", safeStringify({ session, token, user }));
696633
const licenseKeyService = await LicenseKeySingleton.getInstance();
697634
const hasValidLicense = await licenseKeyService.checkLicense();
635+
698636
const profileId = token.profileId;
699637
const calendsoSession: Session = {
700638
...session,

Diff for: packages/lib/constants.ts

-7
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,3 @@ export const RECORDING_IN_PROGRESS_ICON = IS_PRODUCTION
192192
? `${WEBAPP_URL}/stop-recording.svg`
193193
: `https://app.cal.com/stop-recording.svg`;
194194

195-
export const SCOPE_USERINFO_PROFILE = "https://www.googleapis.com/auth/userinfo.profile";
196-
export const SCOPE_USERINFO_EMAIL = "https://www.googleapis.com/auth/userinfo.email";
197-
export const GOOGLE_OAUTH_SCOPES = [SCOPE_USERINFO_PROFILE, SCOPE_USERINFO_EMAIL];
198-
export const GOOGLE_CALENDAR_SCOPES = [
199-
"https://www.googleapis.com/auth/calendar.events",
200-
"https://www.googleapis.com/auth/calendar.readonly",
201-
];

Diff for: packages/lib/google.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { OAuth2Client } from "google-auth-library";
2+
import type { calendar_v3 } from "googleapis";
3+
import { google } from "googleapis";
4+
5+
import logger from "@calcom/lib/logger";
6+
7+
import { UserRepository } from "./server/repository/user";
8+
9+
export async function getAllCalendars(
10+
calendar: calendar_v3.Calendar,
11+
fields: string[] = ["id", "summary", "primary", "accessRole"]
12+
): Promise<calendar_v3.Schema$CalendarListEntry[]> {
13+
let allCalendars: calendar_v3.Schema$CalendarListEntry[] = [];
14+
let pageToken: string | undefined;
15+
16+
try {
17+
do {
18+
const response: any = await calendar.calendarList.list({
19+
fields: `items(${fields.join(",")}),nextPageToken`,
20+
pageToken,
21+
maxResults: 250, // 250 is max
22+
});
23+
24+
allCalendars = [...allCalendars, ...(response.data.items ?? [])];
25+
pageToken = response.data.nextPageToken;
26+
} while (pageToken);
27+
28+
return allCalendars;
29+
} catch (error) {
30+
logger.error("Error fetching all Google Calendars", { error });
31+
throw error;
32+
}
33+
}
34+
35+
export async function updateProfilePhoto(oAuth2Client: OAuth2Client, userId: number) {
36+
try {
37+
const oauth2 = google.oauth2({ version: "v2", auth: oAuth2Client });
38+
const userDetails = await oauth2.userinfo.get();
39+
if (userDetails.data?.picture) {
40+
await UserRepository.updateAvatar({ id: userId, avatarUrl: userDetails.data.picture });
41+
}
42+
} catch (error) {
43+
logger.error("Error updating avatarUrl from google calendar connect", error);
44+
}
45+
}

Diff for: packages/lib/server/repository/credential.ts

-9
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,6 @@ export class CredentialRepository {
3030
});
3131
}
3232

33-
static async findFirstByAppIdAndUserId({ appId, userId }: { appId: string; userId: number }) {
34-
return await prisma.credential.findFirst({
35-
where: {
36-
appId,
37-
userId,
38-
},
39-
});
40-
}
41-
4233
static async findFirstByUserIdAndType({ userId, type }: { userId: number; type: string }) {
4334
return await prisma.credential.findFirst({ where: { userId, type } });
4435
}

0 commit comments

Comments
 (0)