Files
documenso/packages/auth/server/routes/two-factor.ts
ephraimduncan 138d663c25 chore: merge main, resolve biome formatting conflicts
Merge origin/main into feat/external-2fa-codes. Resolve formatting
conflicts caused by biome rollout; preserve both feature streams:
PR's external 2FA token + signing-session 2FA proof additions plus
main's RateLimit/RecipientExpired/signingReminders/date-auto-insert.

In complete-document-with-token.ts, drop the duplicate early
field-fetching block introduced when main moved that logic later
with date auto-insert support; keep the EXTERNAL_TWO_FACTOR_AUTH
check using derivedRecipientActionAuth.
2026-05-12 11:46:11 +00:00

147 lines
3.6 KiB
TypeScript

import { AppError } from '@documenso/lib/errors/app-error';
import { disableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/disable-2fa';
import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/enable-2fa';
import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa';
import { viewBackupCodes } from '@documenso/lib/server-only/2fa/view-backup-codes';
import { prisma } from '@documenso/prisma';
import { sValidator } from '@hono/standard-validator';
import { Hono } from 'hono';
import { AuthenticationErrorCode } from '../lib/errors/error-codes';
import { getSession } from '../lib/utils/get-session';
import type { HonoAuthContext } from '../types/context';
import {
ZDisableTwoFactorRequestSchema,
ZEnableTwoFactorRequestSchema,
ZViewTwoFactorRecoveryCodesRequestSchema,
} from './two-factor.types';
export const twoFactorRoute = new Hono<HonoAuthContext>()
/**
* Setup two factor authentication.
*/
.post('/setup', async (c) => {
const { user } = await getSession(c);
const result = await setupTwoFactorAuthentication({
user,
});
return c.json({
success: true,
secret: result.secret,
uri: result.uri,
});
})
/**
* Enable two factor authentication.
*/
.post('/enable', sValidator('json', ZEnableTwoFactorRequestSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
const { user: sessionUser } = await getSession(c);
const user = await prisma.user.findFirst({
where: {
id: sessionUser.id,
},
select: {
id: true,
email: true,
twoFactorEnabled: true,
twoFactorSecret: true,
},
});
if (!user) {
throw new AppError(AuthenticationErrorCode.InvalidRequest);
}
const { code } = c.req.valid('json');
const result = await enableTwoFactorAuthentication({
user,
code,
requestMetadata,
});
return c.json({
success: true,
recoveryCodes: result.recoveryCodes,
});
})
/**
* Disable two factor authentication.
*/
.post('/disable', sValidator('json', ZDisableTwoFactorRequestSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
const { user: sessionUser } = await getSession(c);
const user = await prisma.user.findFirst({
where: {
id: sessionUser.id,
},
select: {
id: true,
email: true,
twoFactorEnabled: true,
twoFactorSecret: true,
twoFactorBackupCodes: true,
},
});
if (!user) {
throw new AppError(AuthenticationErrorCode.InvalidRequest);
}
const { totpCode, backupCode } = c.req.valid('json');
await disableTwoFactorAuthentication({
user,
totpCode,
backupCode,
requestMetadata,
});
return c.text('OK', 201);
})
/**
* View backup codes.
*/
.post('/view-recovery-codes', sValidator('json', ZViewTwoFactorRecoveryCodesRequestSchema), async (c) => {
const { user: sessionUser } = await getSession(c);
const user = await prisma.user.findFirst({
where: {
id: sessionUser.id,
},
select: {
id: true,
email: true,
twoFactorEnabled: true,
twoFactorSecret: true,
twoFactorBackupCodes: true,
},
});
if (!user) {
throw new AppError(AuthenticationErrorCode.InvalidRequest);
}
const { token } = c.req.valid('json');
const backupCodes = await viewBackupCodes({
user,
token,
});
return c.json({
success: true,
backupCodes,
});
});