Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion apps/extension/entrypoints/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
/// <reference types="chrome" />

import {
validateExtensionOAuthAuthorizeUrl,
validateExtensionOAuthTokenEndpoint,
} from "../../lib/utils";
import type { Booking } from "../../types/bookings.types";
import type { OAuthTokens } from "../../types/oauth";

const DEV_API_KEY = import.meta.env.EXPO_PUBLIC_CAL_API_KEY as string | undefined;
const IS_DEV_MODE = Boolean(DEV_API_KEY && DEV_API_KEY.length > 0);
const BROWSER_TARGET = import.meta.env.BROWSER_TARGET || "chrome";

function compactRedirectUrls(urls: Array<string | undefined>): string[] {
return urls.filter((url): url is string => Boolean(url?.trim()));
}

function getConfiguredOAuthRedirectUrls(): string[] {
const defaultRedirectUrls = compactRedirectUrls([
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI,
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_EU,
]);

switch (BROWSER_TARGET) {
case "firefox":
return compactRedirectUrls([
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_FIREFOX,
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_FIREFOX_EU,
...defaultRedirectUrls,
]);
case "safari":
return compactRedirectUrls([
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_SAFARI,
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_SAFARI_EU,
...defaultRedirectUrls,
]);
case "edge":
return compactRedirectUrls([
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_EDGE,
import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI_EDGE_EU,
...defaultRedirectUrls,
]);
default:
return defaultRedirectUrls;
}
}

function getExpectedOAuthRedirectUrls(): string[] {
return compactRedirectUrls([
getIdentityAPI()?.getRedirectURL?.(),
...getConfiguredOAuthRedirectUrls(),
]);
}

const devLog = {
log: (...args: unknown[]) => IS_DEV_MODE && console.log("[Cal.com]", ...args),
warn: (...args: unknown[]) => IS_DEV_MODE && console.warn("[Cal.com]", ...args),
Expand Down Expand Up @@ -378,6 +423,12 @@ export default defineBackground(() => {

if (message.action === "start-extension-oauth") {
const authUrl = message.authUrl as string;
const expectedRedirectUrls = getExpectedOAuthRedirectUrls();
const authUrlValidation = validateExtensionOAuthAuthorizeUrl(authUrl, expectedRedirectUrls);
if (!authUrlValidation.ok) {
sendResponse({ success: false, error: authUrlValidation.reason });
return true;
}
const state = new URL(authUrl).searchParams.get("state");

// Store state before starting OAuth to prevent race conditions
Expand Down Expand Up @@ -555,7 +606,7 @@ async function handleExtensionOAuth(authUrl: string): Promise<string> {
devLog.log("Expected redirect URL:", expectedRedirectUrl);
devLog.log("Auth URL redirect_uri:", redirectUri);

if (redirectUri && !redirectUri.startsWith(expectedRedirectUrl.replace(/\/$/, ""))) {
if (redirectUri && !validateExtensionOAuthAuthorizeUrl(authUrl, expectedRedirectUrl).ok) {
devLog.warn(
"MISMATCH! redirect_uri does not match expected URL.",
"\nExpected:",
Expand Down Expand Up @@ -781,6 +832,11 @@ async function handleTokenExchange(
tokenEndpoint: string,
state?: string
): Promise<OAuthTokens> {
const tokenEndpointValidation = validateExtensionOAuthTokenEndpoint(tokenEndpoint);
if (!tokenEndpointValidation.ok) {
throw new Error(tokenEndpointValidation.reason);
}

if (state) {
await validateOAuthState(state);
}
Expand Down
Loading
Loading