disable self-hosted 2FA in cloud mode.

This commit is contained in:
Francis Cao committed 2026-08-03 13:46:54 -07:00
1 parent 4eeaf40bbe
commit 8b0492384f
11 files changed
+58 -10

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@ export function App({ children }) {
const config = useConfig();
const { pathname, router, teamId } = useNavigation();
const { isLoading: isTeamLoading, error: teamError } = useTeamQuery(teamId);
const { data: twoFactorStatus } = useTwoFactorStatusQuery(!!user);
const { data: twoFactorStatus } = useTwoFactorStatusQuery(!!user && !config?.cloudMode);
const needsTwoFactorSetup = !!(twoFactorStatus?.isRequired && !twoFactorStatus?.isEnabled);
useEffect(() => {
+5 -1
View File
@@ -1,7 +1,7 @@
import { z } from 'zod';
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { badRequest, forbidden, json } from '@/lib/response';
import { badRequest, forbidden, json, notFound } from '@/lib/response';
import { decryptSecret } from '@/lib/two-factor/crypto';
import { checkRateLimit, recordFailedAttempt, resetRateLimit } from '@/lib/two-factor/rate-limit';
import { isOtpReplayed, markOtpUsed } from '@/lib/two-factor/replay-prevention';
@@ -10,6 +10,10 @@ import { checkPassword } from '@/lib/password';
import { getUser } from '@/queries/prisma/user';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.object({
password: z.string(),
token: z.string().length(6),
+5 -1
View File
@@ -1,8 +1,12 @@
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { json } from '@/lib/response';
import { json, notFound } from '@/lib/response';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const { auth, error } = await parseRequest(request);
if (error) {
+5 -1
View File
@@ -1,7 +1,7 @@
import { z } from 'zod';
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { badRequest, json } from '@/lib/response';
import { badRequest, json, notFound } from '@/lib/response';
import { generateBackupCodes } from '@/lib/two-factor/backup-codes';
import { decryptSecret } from '@/lib/two-factor/crypto';
import { checkRateLimit, recordFailedAttempt, resetRateLimit } from '@/lib/two-factor/rate-limit';
@@ -9,6 +9,10 @@ import { isOtpReplayed, markOtpUsed } from '@/lib/two-factor/replay-prevention';
import { verifyTotp } from '@/lib/two-factor/totp';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.object({ token: z.string().length(6) });
const { auth, body, error } = await parseRequest(request, schema);
+5 -1
View File
@@ -1,5 +1,5 @@
import { parseRequest } from '@/lib/request';
import { badRequest, json } from '@/lib/response';
import { badRequest, json, notFound } from '@/lib/response';
import { encryptSecret } from '@/lib/two-factor/crypto';
import {
generateOtpAuthUri,
@@ -10,6 +10,10 @@ import prisma from '@/lib/prisma';
import { getUser } from '@/queries/prisma/user';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const { auth, error } = await parseRequest(request);
if (error) {
+4
View File
@@ -9,6 +9,10 @@ export async function GET(request: Request) {
return error();
}
if (process.env.CLOUD_MODE) {
return json({ isEnabled: false, isRequired: false, requiredReason: null });
}
const userId = auth.user.id;
const twoFactor = await prisma.client.twoFactorAuth.findUnique({ where: { userId } });
+5 -1
View File
@@ -5,7 +5,7 @@ import { ROLES } from '@/lib/constants';
import { secret } from '@/lib/crypto';
import { createSecureToken, parseSecureToken } from '@/lib/jwt';
import { parseRequest } from '@/lib/request';
import { badRequest, json, unauthorized } from '@/lib/response';
import { badRequest, json, notFound, unauthorized } from '@/lib/response';
import { verifyBackupCode } from '@/lib/two-factor/backup-codes';
import { decryptSecret } from '@/lib/two-factor/crypto';
import { checkRateLimit, recordFailedAttempt, resetRateLimit } from '@/lib/two-factor/rate-limit';
@@ -15,6 +15,10 @@ import { getAllUserTeams, getUser } from '@/queries/prisma';
import redis from '@/lib/redis';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.union([
z.object({ token: z.string().length(6) }).strict(),
z.object({ backupCode: z.string().min(1) }).strict(),
+5 -1
View File
@@ -1,10 +1,14 @@
import { z } from 'zod';
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { json, notFound, unauthorized } from '@/lib/response';
import { canEnforceTwoFactorAuthForEveryone } from '@/permissions';
export async function POST(request: Request) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.object({ required: z.boolean() });
const { auth, body, error } = await parseRequest(request, schema);
@@ -1,10 +1,14 @@
import { z } from 'zod';
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { json, notFound, unauthorized } from '@/lib/response';
import { canEnforceTwoFactorAuthForTeam } from '@/permissions';
export async function POST(request: Request, { params }: { params: Promise<{ teamId: string }> }) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.object({ required: z.boolean() });
const { auth, body, error } = await parseRequest(request, schema);
+13 -1
View File
@@ -1,11 +1,15 @@
import { z } from 'zod';
import prisma from '@/lib/prisma';
import { parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { json, notFound, unauthorized } from '@/lib/response';
import { updateUser } from '@/queries/prisma/user';
import { canEnforceTwoFactorAuthForUser } from '@/permissions';
export async function GET(request: Request, { params }: { params: Promise<{ userId: string }> }) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const { auth, error } = await parseRequest(request);
if (error) {
@@ -24,6 +28,10 @@ export async function GET(request: Request, { params }: { params: Promise<{ user
}
export async function POST(request: Request, { params }: { params: Promise<{ userId: string }> }) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const schema = z.object({ required: z.boolean() });
const { auth, body, error } = await parseRequest(request, schema);
@@ -45,6 +53,10 @@ export async function POST(request: Request, { params }: { params: Promise<{ use
}
export async function DELETE(request: Request, { params }: { params: Promise<{ userId: string }> }) {
if (process.env.CLOUD_MODE) {
return notFound();
}
const { auth, error } = await parseRequest(request);
if (error) {
+5 -1
View File
@@ -31,9 +31,13 @@ export async function POST(request: Request) {
}
const { id, role, createdAt } = user;
const cloudMode = !!process.env.CLOUD_MODE;
// Check if 2FA is enabled for this user
const twoFactor = await prisma.client.twoFactorAuth.findUnique({ where: { userId: id } });
const twoFactor = !cloudMode
? await prisma.client.twoFactorAuth.findUnique({ where: { userId: id } })
: null;
if (twoFactor?.isEnabled) {
const partialToken = createSecureToken({ userId: id, type: 'partial-auth' }, secret(), {
expiresIn: '5m',