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.
This commit is contained in:
ephraimduncan
2026-05-12 11:46:11 +00:00
parent 9194884fbe
commit 138d663c25
1959 changed files with 93488 additions and 47038 deletions
+2 -6
View File
@@ -1,17 +1,13 @@
import { prisma } from '@documenso/prisma';
import type { User } from '@prisma/client';
import { UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { validateTwoFactorAuthentication } from './validate-2fa';
type DisableTwoFactorAuthenticationOptions = {
user: Pick<
User,
'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'
>;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'>;
totpCode?: string;
backupCode?: string;
requestMetadata?: RequestMetadata;
@@ -1,12 +1,10 @@
import { createElement } from 'react';
import { msg } from '@lingui/core/macro';
import { EnvelopeType } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import { AccessAuth2FAEmailTemplate } from '@documenso/email/templates/access-auth-2fa';
import { isRecipientEmailValidForSending } from '@documenso/lib/utils/recipients';
import { prisma } from '@documenso/prisma';
import { msg } from '@lingui/core/macro';
import { EnvelopeType } from '@prisma/client';
import { createElement } from 'react';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
@@ -108,32 +106,29 @@ export const send2FATokenEmail = async ({ token, envelopeId }: Send2FATokenEmail
renderEmailWithI18N(template, { lang: emailLanguage, branding, plainText: true }),
]);
await prisma.$transaction(
async (tx) => {
await mailer.sendMail({
to: {
address: recipient.email,
name: recipient.name,
},
from: senderEmail,
replyTo: replyToEmail,
subject,
html,
text,
});
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_ACCESS_AUTH_2FA_REQUESTED,
envelopeId: envelope.id,
data: {
recipientEmail: recipient.email,
recipientName: recipient.name,
recipientId: recipient.id,
},
}),
});
// Send email outside any transaction to avoid holding a connection
// open during network I/O.
await mailer.sendMail({
to: {
address: recipient.email,
name: recipient.name,
},
{ timeout: 30_000 },
);
from: senderEmail,
replyTo: replyToEmail,
subject,
html,
text,
});
await prisma.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_ACCESS_AUTH_2FA_REQUESTED,
envelopeId: envelope.id,
data: {
recipientEmail: recipient.email,
recipientName: recipient.name,
recipientId: recipient.id,
},
}),
});
};
+1 -2
View File
@@ -1,6 +1,5 @@
import { type User, UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { type User, UserSecurityAuditLogType } from '@prisma/client';
import { AppError } from '../../errors/app-error';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
@@ -25,9 +25,7 @@ export const getBackupCodes = ({ user }: GetBackupCodesOptions) => {
throw new Error('User has no backup codes');
}
const secret = Buffer.from(symmetricDecrypt({ key, data: user.twoFactorBackupCodes })).toString(
'utf-8',
);
const secret = Buffer.from(symmetricDecrypt({ key, data: user.twoFactorBackupCodes })).toString('utf-8');
const data = JSON.parse(secret);
@@ -6,8 +6,6 @@ type IsTwoFactorAuthenticationEnabledOptions = {
user: User;
};
export const isTwoFactorAuthenticationEnabled = ({
user,
}: IsTwoFactorAuthenticationEnabledOptions) => {
export const isTwoFactorAuthenticationEnabled = ({ user }: IsTwoFactorAuthenticationEnabledOptions) => {
return user.twoFactorEnabled && typeof DOCUMENSO_ENCRYPTION_KEY === 'string';
};
+3 -6
View File
@@ -1,10 +1,9 @@
import { type User } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { User } from '@prisma/client';
import { base32 } from '@scure/base';
import crypto from 'crypto';
import { createTOTPKeyURI } from 'oslo/otp';
import { prisma } from '@documenso/prisma';
import { DOCUMENSO_ENCRYPTION_KEY } from '../../constants/crypto';
import { symmetricEncrypt } from '../../universal/crypto';
@@ -14,9 +13,7 @@ type SetupTwoFactorAuthenticationOptions = {
const ISSUER = 'Documenso';
export const setupTwoFactorAuthentication = async ({
user,
}: SetupTwoFactorAuthenticationOptions) => {
export const setupTwoFactorAuthentication = async ({ user }: SetupTwoFactorAuthenticationOptions) => {
const key = DOCUMENSO_ENCRYPTION_KEY;
if (!key) {
+1 -4
View File
@@ -7,10 +7,7 @@ import { verifyBackupCode } from './verify-backup-code';
type ValidateTwoFactorAuthenticationOptions = {
totpCode?: string;
backupCode?: string;
user: Pick<
User,
'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'
>;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'>;
};
export const validateTwoFactorAuthentication = async ({
@@ -30,9 +30,7 @@ export const verifyTwoFactorAuthenticationToken = async ({
throw new Error('user missing 2fa secret');
}
const secret = Buffer.from(symmetricDecrypt({ key, data: user.twoFactorSecret })).toString(
'utf-8',
);
const secret = Buffer.from(symmetricDecrypt({ key, data: user.twoFactorSecret })).toString('utf-8');
const decodedSecret = base32.decode(secret);
@@ -1,6 +1,5 @@
import { compare } from '@node-rs/bcrypt';
import { prisma } from '@documenso/prisma';
import { compare } from '@node-rs/bcrypt';
type VerifyPasswordOptions = {
userId: number;
@@ -5,10 +5,7 @@ import { getBackupCodes } from './get-backup-code';
import { validateTwoFactorAuthentication } from './validate-2fa';
type ViewBackupCodesOptions = {
user: Pick<
User,
'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'
>;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'>;
token: string;
};