Status: Accepted
Date: 2026-06-20
The PDF report endpoint (GET /ai/report/{id}) returns a binary response. An early implementation used an <a href="...?token=..."> link so the browser could initiate the download directly. This embeds the JWT in the URL query string.
JWTs must only be transmitted via the Authorization: Bearer <token> request header. No endpoint in Pulse accepts or reads a token from a URL query parameter.
The PDF download is implemented as:
const res = await fetch(`${API_URL}/ai/report/${patientId}`, {
headers: { Authorization: `Bearer ${token}` },
});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `pulse-report-${patientId}.pdf`;
a.click();
URL.revokeObjectURL(url);Tokens in URLs are:
- Logged verbatim by web servers, reverse proxies, and CDNs
- Stored in browser history and bookmarks
- Leaked via the
Refererheader to third-party origins - Visible in server access logs accessible to anyone with infrastructure access
OWASP ASVS V3.1 and RFC 6750 §5.3 explicitly prohibit bearer tokens in query strings. Even in a demo, establishing this pattern matters for portfolio credibility — the wrong pattern copied into a real app becomes a real vulnerability.
- The PDF download requires JavaScript; it cannot be a plain
<a href>link - The download button shows a loading state (
Generating…) while the fetch is in flight URL.revokeObjectURLis called immediately after the programmatic click to avoid memory leaks