feat: add granular signin disable flags and OIDC auto-redirect (#2857)

This commit is contained in:
Kendry Grullon
2026-06-30 02:08:09 -04:00
committed by GitHub
parent 3b110cf70d
commit 562d78e2d7
28 changed files with 371 additions and 56 deletions
@@ -17,6 +17,7 @@ export const AuthenticationErrorCode = {
// TwoFactorMissingSecret: 'TWO_FACTOR_MISSING_SECRET',
// TwoFactorMissingCredentials: 'TWO_FACTOR_MISSING_CREDENTIALS',
InvalidTwoFactorCode: 'INVALID_TWO_FACTOR_CODE',
SigninDisabled: 'SIGNIN_DISABLED',
SignupDisabled: 'SIGNUP_DISABLED',
SignupDisposableEmail: 'SIGNUP_DISPOSABLE_EMAIL',
// IncorrectTwoFactorBackupCode: 'INCORRECT_TWO_FACTOR_BACKUP_CODE',
@@ -1,6 +1,7 @@
import {
isDisposableEmail,
isEmailDomainAllowedForSignup,
isSigninEnabledForProvider,
isSignupEnabledForProvider,
} from '@documenso/lib/constants/auth';
import { EMAIL_VERIFICATION_STATE } from '@documenso/lib/constants/email';
@@ -64,6 +65,12 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
.post('/authorize', sValidator('json', ZSignInSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
if (!isSigninEnabledForProvider('email')) {
throw new AppError(AuthenticationErrorCode.SigninDisabled, {
statusCode: 400,
});
}
const { email, password, totpCode, backupCode, csrfToken, captchaToken } = c.req.valid('json');
const loginLimitResult = await loginRateLimit.check({
@@ -244,6 +251,12 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
const { password, currentPassword } = c.req.valid('json');
const requestMetadata = c.get('requestMetadata');
if (!isSigninEnabledForProvider('email')) {
throw new AppError(AuthenticationErrorCode.SigninDisabled, {
statusCode: 400,
});
}
const { session, user } = await getSession(c);
await updatePassword({
@@ -346,6 +359,12 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
.post('/forgot-password', sValidator('json', ZForgotPasswordSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
if (!isSigninEnabledForProvider('email')) {
throw new AppError(AuthenticationErrorCode.SigninDisabled, {
statusCode: 400,
});
}
const { email } = c.req.valid('json');
const forgotLimitResult = await forgotPasswordRateLimit.check({
@@ -377,6 +396,12 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
.post('/reset-password', sValidator('json', ZResetPasswordSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
if (!isSigninEnabledForProvider('email')) {
throw new AppError(AuthenticationErrorCode.SigninDisabled, {
statusCode: 400,
});
}
const { token, password } = c.req.valid('json');
const resetLimitResult = await resetPasswordRateLimit.check({
+27
View File
@@ -41,6 +41,14 @@ export const IS_OIDC_SSO_ENABLED = Boolean(
export const OIDC_PROVIDER_LABEL = env('NEXT_PRIVATE_OIDC_PROVIDER_LABEL');
/**
* Opt-out flag for the automatic OIDC redirect.
*
* When OIDC is the only enabled signin transport we redirect to the provider
* automatically. Set this to "true" to keep rendering the signin page instead.
*/
export const IS_OIDC_AUTO_REDIRECT_DISABLED = env('NEXT_PUBLIC_DISABLE_OIDC_AUTO_REDIRECT') === 'true';
export const USER_SECURITY_AUDIT_LOG_MAP: Record<string, string> = {
ACCOUNT_SSO_LINK: 'Linked account to SSO',
ACCOUNT_SSO_UNLINK: 'Unlinked account from SSO',
@@ -188,3 +196,22 @@ export const isSignupEnabledForProvider = (provider: 'email' | 'google' | 'micro
return env(flagMap[provider]) !== 'true';
};
/**
* Check if signin is enabled for the given provider.
* The master switch takes precedence over the per-provider flags.
*/
export const isSigninEnabledForProvider = (provider: 'email' | 'google' | 'microsoft' | 'oidc'): boolean => {
if (env('NEXT_PUBLIC_DISABLE_SIGNIN') === 'true') {
return false;
}
const flagMap = {
email: 'NEXT_PUBLIC_DISABLE_EMAIL_PASSWORD_SIGNIN',
google: 'NEXT_PUBLIC_DISABLE_GOOGLE_SIGNIN',
microsoft: 'NEXT_PUBLIC_DISABLE_MICROSOFT_SIGNIN',
oidc: 'NEXT_PUBLIC_DISABLE_OIDC_SIGNIN',
} as const;
return env(flagMap[provider]) !== 'true';
};
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "Weiterleitungs-URL"
msgid "Redirecting"
msgstr "Weiterleitung"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8908,6 +8908,11 @@ msgstr "Redirect URL"
msgid "Redirecting"
msgstr "Redirecting"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr "Redirecting to {0}..."
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "URL de redirección"
msgid "Redirecting"
msgstr "Redireccionando"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "URL de redirection"
msgid "Redirecting"
msgstr "Redirection"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "URL di reindirizzamento"
msgid "Redirecting"
msgstr "Reindirizzamento"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "リダイレクト URL"
msgid "Redirecting"
msgstr "リダイレクト中"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "리디렉션 URL"
msgid "Redirecting"
msgstr "리디렉션 중"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "Redirect-URL"
msgid "Redirecting"
msgstr "Doorsturen"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8918,6 +8918,11 @@ msgstr "Adres URL przekierowania"
msgid "Redirecting"
msgstr "Przekierowywanie"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8908,6 +8908,11 @@ msgstr "URL de Redirecionamento"
msgid "Redirecting"
msgstr "Redirecionando"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+5
View File
@@ -8917,6 +8917,11 @@ msgstr "重定向 URL"
msgid "Redirecting"
msgstr "正在重定向"
#. placeholder {0}: oidcProviderLabel || 'OIDC'
#: apps/remix/app/routes/_unauthenticated+/signin.tsx
msgid "Redirecting to {0}..."
msgstr ""
#: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/general/claim-account.tsx
msgid "Registration Successful"
+7
View File
@@ -93,6 +93,13 @@ declare namespace NodeJS {
NEXT_PUBLIC_DISABLE_OIDC_SIGNUP?: string;
NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS?: string;
NEXT_PUBLIC_DISABLE_SIGNIN?: string;
NEXT_PUBLIC_DISABLE_EMAIL_PASSWORD_SIGNIN?: string;
NEXT_PUBLIC_DISABLE_GOOGLE_SIGNIN?: string;
NEXT_PUBLIC_DISABLE_MICROSOFT_SIGNIN?: string;
NEXT_PUBLIC_DISABLE_OIDC_SIGNIN?: string;
NEXT_PUBLIC_DISABLE_OIDC_AUTO_REDIRECT?: string;
NEXT_PRIVATE_BROWSERLESS_URL?: string;
NEXT_PRIVATE_JOBS_PROVIDER?: 'inngest' | 'local' | 'bullmq';