mirror of
https://github.com/documenso/documenso.git
synced 2025-11-09 20:12:31 +10:00
Adds support for 2FA when completing a document, also adds support for using email for 2FA when no authenticator has been associated with the account.
95 lines
2.6 KiB
TypeScript
95 lines
2.6 KiB
TypeScript
import { TRPCError } from '@trpc/server';
|
|
import { DateTime } from 'luxon';
|
|
|
|
import { TWO_FACTOR_EMAIL_EXPIRATION_MINUTES } from '@documenso/lib/server-only/2fa/email/constants';
|
|
import { send2FATokenEmail } from '@documenso/lib/server-only/2fa/email/send-2fa-token-email';
|
|
import { DocumentAuth } from '@documenso/lib/types/document-auth';
|
|
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
|
import { prisma } from '@documenso/prisma';
|
|
|
|
import { procedure } from '../trpc';
|
|
import {
|
|
ZAccessAuthRequest2FAEmailRequestSchema,
|
|
ZAccessAuthRequest2FAEmailResponseSchema,
|
|
} from './access-auth-request-2fa-email.types';
|
|
|
|
export const accessAuthRequest2FAEmailRoute = procedure
|
|
.input(ZAccessAuthRequest2FAEmailRequestSchema)
|
|
.output(ZAccessAuthRequest2FAEmailResponseSchema)
|
|
.mutation(async ({ input, ctx }) => {
|
|
try {
|
|
const { token } = input;
|
|
|
|
const user = ctx.user;
|
|
|
|
// Get document and recipient by token
|
|
const document = await prisma.document.findFirst({
|
|
where: {
|
|
recipients: {
|
|
some: {
|
|
token,
|
|
},
|
|
},
|
|
},
|
|
include: {
|
|
recipients: {
|
|
where: {
|
|
token,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!document) {
|
|
throw new TRPCError({
|
|
code: 'NOT_FOUND',
|
|
message: 'Document not found',
|
|
});
|
|
}
|
|
|
|
const [recipient] = document.recipients;
|
|
|
|
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
|
documentAuth: document.authOptions,
|
|
recipientAuth: recipient.authOptions,
|
|
});
|
|
|
|
if (!derivedRecipientAccessAuth.includes(DocumentAuth.TWO_FACTOR_AUTH)) {
|
|
throw new TRPCError({
|
|
code: 'BAD_REQUEST',
|
|
message: '2FA is not required for this document',
|
|
});
|
|
}
|
|
|
|
// if (user && recipient.email !== user.email) {
|
|
// throw new TRPCError({
|
|
// code: 'UNAUTHORIZED',
|
|
// message: 'User does not match recipient',
|
|
// });
|
|
// }
|
|
|
|
const expiresAt = DateTime.now().plus({ minutes: TWO_FACTOR_EMAIL_EXPIRATION_MINUTES });
|
|
|
|
await send2FATokenEmail({
|
|
token,
|
|
documentId: document.id,
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
expiresAt: expiresAt.toJSDate(),
|
|
};
|
|
} catch (error) {
|
|
console.error('Error sending access auth 2FA email:', error);
|
|
|
|
if (error instanceof TRPCError) {
|
|
throw error;
|
|
}
|
|
|
|
throw new TRPCError({
|
|
code: 'INTERNAL_SERVER_ERROR',
|
|
message: 'Failed to send 2FA email',
|
|
});
|
|
}
|
|
});
|