feat(06-05): download token utilities + download API route

- Add createDownloadToken and verifyDownloadToken to token.ts (15-min TTL, purpose:'download' claim)
- Create GET /api/sign/[token]/download route: validates dt query param JWT, streams signedFilePath as PDF
- Path traversal guard: signedFilePath must start with UPLOADS_DIR
- Auto-fix: Buffer cast to Uint8Array for Response BodyInit compatibility (Next.js 16 / TypeScript strict)
This commit is contained in:
Chandler Copeland
2026-03-20 11:41:18 -06:00
parent 5c1ea3568e
commit a276da0da1
2 changed files with 100 additions and 0 deletions

View File

@@ -30,3 +30,19 @@ export async function verifySigningToken(token: string): Promise<{ documentId: s
const { payload } = await jwtVerify(token, getSecret());
return payload as { documentId: string; jti: string; exp: number };
}
// Short-lived download token for client copy download (15-min TTL, no DB record)
// purpose: 'download' claim distinguishes from signing tokens
export async function createDownloadToken(documentId: string): Promise<string> {
return await new SignJWT({ documentId, purpose: 'download' })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('15m')
.sign(getSecret());
}
export async function verifyDownloadToken(token: string): Promise<{ documentId: string }> {
const { payload } = await jwtVerify(token, getSecret());
if (payload['purpose'] !== 'download') throw new Error('Not a download token');
return { documentId: payload['documentId'] as string };
}