feat: add granular signup disable flags (#2765)

This commit is contained in:
Ephraim Duncan
2026-05-09 01:16:13 +00:00
committed by GitHub
parent ec8728b33e
commit a197bf113f
18 changed files with 245 additions and 116 deletions
+88 -76
View File
@@ -58,18 +58,20 @@ export type TSignUpFormSchema = z.infer<typeof ZSignUpFormSchema>;
export type SignUpFormProps = {
className?: string;
initialEmail?: string;
isGoogleSSOEnabled?: boolean;
isMicrosoftSSOEnabled?: boolean;
isOIDCSSOEnabled?: boolean;
isEmailPasswordSignupEnabled?: boolean;
isGoogleSignupEnabled?: boolean;
isMicrosoftSignupEnabled?: boolean;
isOidcSignupEnabled?: boolean;
returnTo?: string;
};
export const SignUpForm = ({
className,
initialEmail,
isGoogleSSOEnabled,
isMicrosoftSSOEnabled,
isOIDCSSOEnabled,
isEmailPasswordSignupEnabled = true,
isGoogleSignupEnabled,
isMicrosoftSignupEnabled,
isOidcSignupEnabled,
returnTo,
}: SignUpFormProps) => {
const { _ } = useLingui();
@@ -86,7 +88,7 @@ export const SignUpForm = ({
const [captchaToken, setCaptchaToken] = useState<string | null>(null);
const hasSocialAuthEnabled = isGoogleSSOEnabled || isMicrosoftSSOEnabled || isOIDCSSOEnabled;
const hasSocialAuthEnabled = isGoogleSignupEnabled || isMicrosoftSignupEnabled || isOidcSignupEnabled;
const form = useForm<TSignUpFormSchema>({
values: {
@@ -145,7 +147,7 @@ export const SignUpForm = ({
const onSignUpWithGoogleClick = async () => {
try {
await authClient.google.signIn();
} catch (err) {
} catch {
toast({
title: _(msg`An unknown error occurred`),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
@@ -157,7 +159,7 @@ export const SignUpForm = ({
const onSignUpWithMicrosoftClick = async () => {
try {
await authClient.microsoft.signIn();
} catch (err) {
} catch {
toast({
title: _(msg`An unknown error occurred`),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
@@ -169,7 +171,7 @@ export const SignUpForm = ({
const onSignUpWithOIDCClick = async () => {
try {
await authClient.oidc.signIn();
} catch (err) {
} catch {
toast({
title: _(msg`An unknown error occurred`),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
@@ -235,72 +237,80 @@ export const SignUpForm = ({
<Form {...form}>
<form className="flex w-full flex-1 flex-col gap-y-4" onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Full Name</Trans>
</FormLabel>
<FormControl>
<Input type="text" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{isEmailPasswordSignupEnabled && (
<>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Full Name</Trans>
</FormLabel>
<FormControl>
<Input type="text" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Email Address</Trans>
</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Email Address</Trans>
</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Password</Trans>
</FormLabel>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>
<Trans>Password</Trans>
</FormLabel>
<FormControl>
<PasswordInput {...field} />
</FormControl>
<FormControl>
<PasswordInput {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="signature"
render={({ field: { onChange, value } }) => (
<FormItem>
<FormLabel>
<Trans>Sign Here</Trans>
</FormLabel>
<FormControl>
<SignaturePadDialog disabled={isSubmitting} value={value} onChange={(v) => onChange(v ?? '')} />
</FormControl>
<FormField
control={form.control}
name="signature"
render={({ field: { onChange, value } }) => (
<FormItem>
<FormLabel>
<Trans>Sign Here</Trans>
</FormLabel>
<FormControl>
<SignaturePadDialog
disabled={isSubmitting}
value={value}
onChange={(v) => onChange(v ?? '')}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormMessage />
</FormItem>
)}
/>
</>
)}
{turnstileSiteKey && (
<Turnstile
@@ -325,7 +335,7 @@ export const SignUpForm = ({
</div>
)}
{isGoogleSSOEnabled && (
{isGoogleSignupEnabled && (
<Button
type="button"
size="lg"
@@ -339,7 +349,7 @@ export const SignUpForm = ({
</Button>
)}
{isMicrosoftSSOEnabled && (
{isMicrosoftSignupEnabled && (
<Button
type="button"
size="lg"
@@ -353,7 +363,7 @@ export const SignUpForm = ({
</Button>
)}
{isOIDCSSOEnabled && (
{isOidcSignupEnabled && (
<Button
type="button"
size="lg"
@@ -377,9 +387,11 @@ export const SignUpForm = ({
</p>
</fieldset>
<Button loading={form.formState.isSubmitting} type="submit" size="lg" className="mt-6 w-full">
<Trans>Create account</Trans>
</Button>
{isEmailPasswordSignupEnabled && (
<Button loading={form.formState.isSubmitting} type="submit" size="lg" className="mt-6 w-full">
<Trans>Create account</Trans>
</Button>
)}
</form>
</Form>
<p className="mt-6 text-muted-foreground text-xs">
@@ -1,6 +1,7 @@
import signingCelebration from '@documenso/assets/images/signing-celebration.png';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
import { isSignupEnabledForProvider } from '@documenso/lib/constants/auth';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { isRecipientAuthorized } from '@documenso/lib/server-only/document/is-recipient-authorized';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
@@ -8,7 +9,6 @@ import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-re
import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures';
import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-email';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { env } from '@documenso/lib/utils/env';
import { trpc } from '@documenso/trpc/react';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
import { SigningCard3D } from '@documenso/ui/components/signing-card';
@@ -77,7 +77,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
const recipientName =
recipient.name || fields.find((field) => field.type === FieldType.NAME)?.customText || recipient.email;
const canSignUp = !isExistingUser && env('NEXT_PUBLIC_DISABLE_SIGNUP') !== 'true';
const canSignUp = !isExistingUser && isSignupEnabledForProvider('email');
const canRedirectToFolder = user && document.userId === user.id && document.folderId && document.team?.url;
@@ -3,9 +3,9 @@ import {
IS_GOOGLE_SSO_ENABLED,
IS_MICROSOFT_SSO_ENABLED,
IS_OIDC_SSO_ENABLED,
isSignupEnabledForProvider,
OIDC_PROVIDER_LABEL,
} from '@documenso/lib/constants/auth';
import { env } from '@documenso/lib/utils/env';
import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-valid-return-to';
import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
import { msg } from '@lingui/core/macro';
@@ -32,6 +32,11 @@ export async function loader({ request }: Route.LoaderArgs) {
const isMicrosoftSSOEnabled = IS_MICROSOFT_SSO_ENABLED;
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
const isSignupEnabled =
isSignupEnabledForProvider('email') ||
(IS_GOOGLE_SSO_ENABLED && isSignupEnabledForProvider('google')) ||
(IS_MICROSOFT_SSO_ENABLED && isSignupEnabledForProvider('microsoft')) ||
(IS_OIDC_SSO_ENABLED && isSignupEnabledForProvider('oidc'));
let returnTo = new URL(request.url).searchParams.get('returnTo') ?? undefined;
@@ -45,13 +50,15 @@ export async function loader({ request }: Route.LoaderArgs) {
isGoogleSSOEnabled,
isMicrosoftSSOEnabled,
isOIDCSSOEnabled,
isSignupEnabled,
oidcProviderLabel,
returnTo,
};
}
export default function SignIn({ loaderData }: Route.ComponentProps) {
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel, returnTo } = loaderData;
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, isSignupEnabled, oidcProviderLabel, returnTo } =
loaderData;
const { _ } = useLingui();
@@ -95,7 +102,7 @@ export default function SignIn({ loaderData }: Route.ComponentProps) {
returnTo={returnTo}
/>
{!isEmbeddedRedirect && env('NEXT_PUBLIC_DISABLE_SIGNUP') !== 'true' && (
{!isEmbeddedRedirect && isSignupEnabled && (
<p className="mt-6 text-center text-muted-foreground text-sm">
<Trans>
Don't have an account?{' '}
@@ -1,5 +1,9 @@
import { IS_GOOGLE_SSO_ENABLED, IS_MICROSOFT_SSO_ENABLED, IS_OIDC_SSO_ENABLED } from '@documenso/lib/constants/auth';
import { env } from '@documenso/lib/utils/env';
import {
IS_GOOGLE_SSO_ENABLED,
IS_MICROSOFT_SSO_ENABLED,
IS_OIDC_SSO_ENABLED,
isSignupEnabledForProvider,
} from '@documenso/lib/constants/auth';
import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-valid-return-to';
import { msg } from '@lingui/core/macro';
import { redirect } from 'react-router';
@@ -14,14 +18,15 @@ export function meta() {
}
export function loader({ request }: Route.LoaderArgs) {
const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP');
const isEmailPasswordSignupEnabled = isSignupEnabledForProvider('email');
const isGoogleSignupEnabled = IS_GOOGLE_SSO_ENABLED && isSignupEnabledForProvider('google');
const isMicrosoftSignupEnabled = IS_MICROSOFT_SSO_ENABLED && isSignupEnabledForProvider('microsoft');
const isOidcSignupEnabled = IS_OIDC_SSO_ENABLED && isSignupEnabledForProvider('oidc');
// SSR env variables.
const isGoogleSSOEnabled = IS_GOOGLE_SSO_ENABLED;
const isMicrosoftSSOEnabled = IS_MICROSOFT_SSO_ENABLED;
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
const isAnySignupEnabled =
isEmailPasswordSignupEnabled || isGoogleSignupEnabled || isMicrosoftSignupEnabled || isOidcSignupEnabled;
if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') {
if (!isAnySignupEnabled) {
throw redirect('/signin');
}
@@ -30,22 +35,30 @@ export function loader({ request }: Route.LoaderArgs) {
returnTo = isValidReturnTo(returnTo) ? normalizeReturnTo(returnTo) : undefined;
return {
isGoogleSSOEnabled,
isMicrosoftSSOEnabled,
isOIDCSSOEnabled,
isEmailPasswordSignupEnabled,
isGoogleSignupEnabled,
isMicrosoftSignupEnabled,
isOidcSignupEnabled,
returnTo,
};
}
export default function SignUp({ loaderData }: Route.ComponentProps) {
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, returnTo } = loaderData;
const {
isEmailPasswordSignupEnabled,
isGoogleSignupEnabled,
isMicrosoftSignupEnabled,
isOidcSignupEnabled,
returnTo,
} = loaderData;
return (
<SignUpForm
className="w-screen max-w-screen-2xl px-4 md:px-16 lg:-my-16"
isGoogleSSOEnabled={isGoogleSSOEnabled}
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
isOIDCSSOEnabled={isOIDCSSOEnabled}
isEmailPasswordSignupEnabled={isEmailPasswordSignupEnabled}
isGoogleSignupEnabled={isGoogleSignupEnabled}
isMicrosoftSignupEnabled={isMicrosoftSignupEnabled}
isOidcSignupEnabled={isOidcSignupEnabled}
returnTo={returnTo}
/>
);