From d24f67d922350aa0921e937f5299196cc04dfdbd Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 10 Feb 2025 03:33:22 +1100 Subject: [PATCH] fix: wip --- packages/auth/server/routes/google.ts | 67 +++++++++++++++---- .../server-only/pdf/insert-field-in-pdf.ts | 13 ++-- .../primitives/document-flow/field-icon.tsx | 12 +--- .../primitives/document-flow/field-item.tsx | 16 +---- .../document-flow/show-field-item.tsx | 17 +---- 5 files changed, 67 insertions(+), 58 deletions(-) diff --git a/packages/auth/server/routes/google.ts b/packages/auth/server/routes/google.ts index deb1f7bbe..994964a4a 100644 --- a/packages/auth/server/routes/google.ts +++ b/packages/auth/server/routes/google.ts @@ -8,6 +8,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { env } from '@documenso/lib/utils/env'; import { prisma } from '@documenso/prisma'; +import { UserSecurityAuditLogType } from '@documenso/prisma/client'; import { AuthenticationErrorCode } from '../lib/errors/error-codes'; import { sessionCookieOptions } from '../lib/session/session-cookies'; @@ -71,6 +72,8 @@ export const googleRoute = new Hono() * Google callback verification. */ .get('/callback', async (c) => { + const requestMeta = c.get('requestMetadata'); + const code = c.req.query('code'); const state = c.req.query('state'); @@ -100,6 +103,7 @@ export const googleRoute = new Hono() const claims = decodeIdToken(tokens.idToken()) as Record; const googleEmail = claims.email; + const googleEmailVerified = claims.email_verified; const googleName = claims.name; const googleSub = claims.sub; @@ -145,20 +149,59 @@ export const googleRoute = new Hono() // Handle existing user but no account. if (userWithSameEmail) { - await prisma.account.create({ - data: { - type: 'oauth', - provider: 'google', - providerAccountId: googleSub, - access_token: accessToken, - expires_at: Math.floor(accessTokenExpiresAt.getTime() / 1000), - token_type: 'Bearer', - id_token: idToken, - userId: userWithSameEmail.id, - }, + await prisma.$transaction(async (tx) => { + await tx.account.create({ + data: { + type: 'oauth', + provider: 'google', + providerAccountId: googleSub, + access_token: accessToken, + expires_at: Math.floor(accessTokenExpiresAt.getTime() / 1000), + token_type: 'Bearer', + id_token: idToken, + userId: userWithSameEmail.id, + }, + }); + + // Log link event. + await tx.userSecurityAuditLog.create({ + data: { + userId: userWithSameEmail.id, + ipAddress: requestMeta.ipAddress, + userAgent: requestMeta.userAgent, + type: UserSecurityAuditLogType.ACCOUNT_SSO_LINK, + }, + }); + + // If account already exists in an unverified state, remove the password to ensure + // they cannot sign in since we cannot confirm the password was set by the user. + if (!userWithSameEmail.emailVerified) { + await tx.user.update({ + where: { + id: userWithSameEmail.id, + }, + data: { + emailVerified: new Date(), + password: null, // Todo: Check this + }, + }); + } + + // Apparently incredibly rare case? So we whole account to unverified. + if (!googleEmailVerified) { + // Todo: Add logging. + + await tx.user.update({ + where: { + id: userWithSameEmail.id, + }, + data: { + emailVerified: null, + }, + }); + } }); - // Todo: Link account await onAuthorize({ userId: userWithSameEmail.id }, c); return c.redirect(redirectPath, 302); diff --git a/packages/lib/server-only/pdf/insert-field-in-pdf.ts b/packages/lib/server-only/pdf/insert-field-in-pdf.ts index 3c901c492..c81372179 100644 --- a/packages/lib/server-only/pdf/insert-field-in-pdf.ts +++ b/packages/lib/server-only/pdf/insert-field-in-pdf.ts @@ -15,6 +15,7 @@ import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox'; import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { ZCheckboxFieldMeta, ZDateFieldMeta, @@ -26,14 +27,12 @@ import { ZTextFieldMeta, } from '../../types/field-meta'; -// Todo: Check if this is okay to do compared to the old approach. -// import fontCaveat from '@documenso/assets/fonts/caveat.ttf?inline'; -// import fontNoto from '@documenso/assets/fonts/noto-sans.ttf?inline'; - -const fontCaveat = ''; -const fontNoto = ''; - export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignature) => { + const [fontCaveat, fontNoto] = await Promise.all([ + fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/caveat.ttf`).then(async (res) => res.arrayBuffer()), + fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/noto-sans.ttf`).then(async (res) => res.arrayBuffer()), + ]); + const isSignatureField = isSignatureFieldType(field.type); pdf.registerFontkit(fontkit); diff --git a/packages/ui/primitives/document-flow/field-icon.tsx b/packages/ui/primitives/document-flow/field-icon.tsx index 5deb3dce1..851c9d4d4 100644 --- a/packages/ui/primitives/document-flow/field-icon.tsx +++ b/packages/ui/primitives/document-flow/field-icon.tsx @@ -19,8 +19,6 @@ import { cn } from '../../lib/utils'; type FieldIconProps = { fieldMeta: FieldMetaType; type: FieldType; - signerEmail?: string; - fontCaveatClassName?: string; }; const fieldIcons = { @@ -35,18 +33,12 @@ const fieldIcons = { [FieldType.DROPDOWN]: { icon: ChevronDown, label: 'Select' }, }; -export const FieldIcon = ({ - fieldMeta, - type, - signerEmail, - fontCaveatClassName, -}: FieldIconProps) => { +export const FieldIcon = ({ fieldMeta, type }: FieldIconProps) => { if (type === 'SIGNATURE' || type === 'FREE_SIGNATURE') { return (
Signature diff --git a/packages/ui/primitives/document-flow/field-item.tsx b/packages/ui/primitives/document-flow/field-item.tsx index 46bec4b9f..3682f84a2 100644 --- a/packages/ui/primitives/document-flow/field-item.tsx +++ b/packages/ui/primitives/document-flow/field-item.tsx @@ -1,7 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -// Todo -// import { Caveat } from 'next/font/google'; import { CopyPlus, Settings2, Trash } from 'lucide-react'; import { createPortal } from 'react-dom'; import { Rnd } from 'react-rnd'; @@ -20,13 +18,6 @@ import type { TDocumentFlowFormSchema } from './types'; type Field = TDocumentFlowFormSchema['fields'][0]; -// const fontCaveat = Caveat({ -// weight: ['500'], -// subsets: ['latin'], -// display: 'swap', -// variable: '--font-caveat', -// }); - export type FieldItemProps = { field: Field; passive?: boolean; @@ -252,12 +243,7 @@ export const FieldItem = ({ .with('CHECKBOX', () => ) .with('RADIO', () => ) .otherwise(() => ( - + ))} {!hideRecipients && ( diff --git a/packages/ui/primitives/document-flow/show-field-item.tsx b/packages/ui/primitives/document-flow/show-field-item.tsx index 4fefe8f06..ef21465f8 100644 --- a/packages/ui/primitives/document-flow/show-field-item.tsx +++ b/packages/ui/primitives/document-flow/show-field-item.tsx @@ -1,6 +1,5 @@ -// import { Caveat } from 'next/font/google'; import { useLingui } from '@lingui/react'; -import type { Prisma } from '@prisma/client'; +import { FieldType, type Prisma } from '@prisma/client'; import { createPortal } from 'react-dom'; import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords'; @@ -10,26 +9,16 @@ import { cn } from '../../lib/utils'; import { Card, CardContent } from '../card'; import { FRIENDLY_FIELD_TYPE } from './types'; -// const fontCaveat = Caveat({ -// weight: ['500'], -// subsets: ['latin'], -// display: 'swap', -// variable: '--font-caveat', -// }); - export type ShowFieldItemProps = { field: Prisma.FieldGetPayload; recipients: Prisma.RecipientGetPayload[]; }; -export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => { +export const ShowFieldItem = ({ field }: ShowFieldItemProps) => { const { _ } = useLingui(); const coords = useFieldPageCoords(field); - const signerEmail = - recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? ''; - return createPortal(
{ {parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])}