diff --git a/teressa-copeland-homes/src/lib/signing/token.ts b/teressa-copeland-homes/src/lib/signing/token.ts index fa719cf..4606099 100644 --- a/teressa-copeland-homes/src/lib/signing/token.ts +++ b/teressa-copeland-homes/src/lib/signing/token.ts @@ -4,7 +4,7 @@ import { signingTokens } from '@/lib/db/schema'; const getSecret = () => new TextEncoder().encode(process.env.SIGNING_JWT_SECRET!); -export async function createSigningToken(documentId: string): Promise<{ token: string; jti: string; expiresAt: Date }> { +export async function createSigningToken(documentId: string, signerEmail?: string): Promise<{ token: string; jti: string; expiresAt: Date }> { const jti = crypto.randomUUID(); const expiresAt = new Date(Date.now() + 72 * 60 * 60 * 1000); // 72 hours @@ -19,6 +19,7 @@ export async function createSigningToken(documentId: string): Promise<{ token: s await db.insert(signingTokens).values({ jti, documentId, + signerEmail: signerEmail ?? null, expiresAt, }); @@ -62,3 +63,19 @@ export async function verifyAgentDownloadToken(token: string): Promise<{ documen if (payload['purpose'] !== 'agent-download') throw new Error('Not an agent download token'); return { documentId: payload['documentId'] as string }; } + +// Signer download token — purpose: 'signer-download', 72h TTL, no DB record +// Sent to signers on completion so they can download the fully signed PDF. +export async function createSignerDownloadToken(documentId: string): Promise { + return await new SignJWT({ documentId, purpose: 'signer-download' }) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime('72h') + .sign(getSecret()); +} + +export async function verifySignerDownloadToken(token: string): Promise<{ documentId: string }> { + const { payload } = await jwtVerify(token, getSecret()); + if (payload['purpose'] !== 'signer-download') throw new Error('Not a signer download token'); + return { documentId: payload['documentId'] as string }; +}