Files
calendar/apps/web/lib/signup/getServerSideProps.tsx
T
Peer RichelsenGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>unknownAnik Dhabal Babu
a63802dea7 feat: redirect authenticated users from signup page (#23766)
* feat: redirect authenticated users from signup page

- Add session check in signup getServerSideProps
- Redirect to redirect parameter if present, otherwise to root
- Follows existing authentication patterns in codebase

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix test

* fix test

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: unknown <adhabal2002@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-09-12 23:36:00 +05:30

207 lines
6.4 KiB
TypeScript

import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { getOrgUsernameFromEmail } from "@calcom/features/auth/signup/utils/getOrgUsernameFromEmail";
import { checkPremiumUsername } from "@calcom/features/ee/common/lib/checkPremiumUsername";
import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { IS_SELF_HOSTED, WEBAPP_URL } from "@calcom/lib/constants";
import { emailSchema } from "@calcom/lib/emailSchema";
import slugify from "@calcom/lib/slugify";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants";
const checkValidEmail = (email: string) => emailSchema.safeParse(email).success;
const querySchema = z.object({
username: z
.string()
.optional()
.transform((val) => val || ""),
email: emailSchema.optional(),
});
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
const featuresRepository = new FeaturesRepository(prisma);
const emailVerificationEnabled = await featuresRepository.checkIfFeatureIsEnabledGlobally(
"email-verification"
);
const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");
const token = z.string().optional().parse(ctx.query.token);
const redirectUrlData = z
.string()
.refine((value) => value.startsWith(WEBAPP_URL), {
params: (value: string) => ({ value }),
message: "Redirect URL must start with 'cal.com'",
})
.optional()
.safeParse(ctx.query.redirect);
const redirectUrl = redirectUrlData.success && redirectUrlData.data ? redirectUrlData.data : null;
const session = await getServerSession({
req: ctx.req,
});
if (session?.user?.id) {
return {
redirect: {
permanent: false,
destination: redirectUrl || "/",
},
} as const;
}
const props = {
redirectUrl,
isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED,
isSAMLLoginEnabled,
prepopulateFormValues: undefined,
emailVerificationEnabled,
};
if ((process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" && !token) || signupDisabled) {
return {
redirect: {
permanent: false,
destination: `/auth/error?error=Signup is disabled in this instance`,
},
} as const;
}
// no token given, treat as a normal signup without verification token
if (!token) {
// username + email prepopulated from query params
const queryData = querySchema.safeParse(ctx.query);
return {
props: JSON.parse(
JSON.stringify({
...props,
prepopulateFormValues: {
username: queryData.success ? queryData.data.username : null,
email: queryData.success ? queryData.data.email : null,
},
})
),
};
}
const verificationToken = await prisma.verificationToken.findUnique({
where: {
token,
},
include: {
team: {
select: {
metadata: true,
isOrganization: true,
parentId: true,
parent: {
select: {
slug: true,
isOrganization: true,
organizationSettings: true,
},
},
slug: true,
organizationSettings: true,
},
},
},
});
if (!verificationToken || verificationToken.expires < new Date()) {
return {
redirect: {
permanent: false,
destination: `/auth/error?error=Verification Token is missing or has expired`,
},
} as const;
}
const existingUser = await prisma.user.findFirst({
where: {
AND: [
{
email: verificationToken?.identifier,
},
{
emailVerified: {
not: null,
},
},
],
},
});
if (existingUser) {
return {
redirect: {
permanent: false,
destination: `/auth/login?callbackUrl=${WEBAPP_URL}/${ctx.query.callbackUrl}`,
},
};
}
const guessUsernameFromEmail = (email: string) => {
const [username] = email.split("@");
return username;
};
let username = guessUsernameFromEmail(verificationToken.identifier);
const tokenTeam = {
...verificationToken?.team,
metadata: teamMetadataSchema.parse(verificationToken?.team?.metadata),
};
const isATeamInOrganization = tokenTeam?.parentId !== null;
// Detect if the team is an org by either the metadata flag or if it has a parent team
const isOrganization = tokenTeam.isOrganization;
const isOrganizationOrATeamInOrganization = isOrganization || isATeamInOrganization;
// If we are dealing with an org, the slug may come from the team itself or its parent
const orgSlug = isOrganizationOrATeamInOrganization
? tokenTeam.metadata?.requestedSlug || tokenTeam.parent?.slug || tokenTeam.slug
: null;
// Org context shouldn't check if a username is premium
if (!IS_SELF_HOSTED && !isOrganizationOrATeamInOrganization) {
// Im not sure we actually hit this because of next redirects signup to website repo - but just in case this is pretty cool :)
const { available, suggestion } = await checkPremiumUsername(username);
username = available ? username : suggestion || username;
}
const isValidEmail = checkValidEmail(verificationToken.identifier);
const isOrgInviteByLink = isOrganizationOrATeamInOrganization && !isValidEmail;
const parentOrgSettings = tokenTeam?.parent?.organizationSettings ?? null;
return {
props: {
...props,
token,
prepopulateFormValues: !isOrgInviteByLink
? {
email: verificationToken.identifier,
username: isOrganizationOrATeamInOrganization
? getOrgUsernameFromEmail(
verificationToken.identifier,
(isOrganization
? tokenTeam.organizationSettings?.orgAutoAcceptEmail
: parentOrgSettings?.orgAutoAcceptEmail) || ""
)
: slugify(username),
}
: null,
orgSlug,
orgAutoAcceptEmail: isOrgInviteByLink
? tokenTeam?.organizationSettings?.orgAutoAcceptEmail ?? parentOrgSettings?.orgAutoAcceptEmail ?? null
: null,
},
};
};