feat(05-09): authenticated document preview via fetch + Blob URL

- Add fetchDocumentContent() to client.js: fetch with Bearer auth, 401 refresh
  retry pattern, returns raw Response (not parsed JSON) for blob() calls
- Replace iframe :src=proxyUrl (unauthenticated) in DocumentPreviewModal.vue
  with authenticated fetch → blob → URL.createObjectURL; loading/error states;
  URL.revokeObjectURL on unmount to prevent memory leaks
- Replace window.open(rawUrl) in DocumentView.vue openPdf() with
  fetchDocumentContent → blob → objectUrl → window.open; 60s auto-revoke
- Frontend build exits 0 with zero errors
- Closes T-05-09-04: no persistent unauthenticated content exposure
This commit is contained in:
curo1305
2026-05-30 11:18:01 +02:00
parent 6d094d17f0
commit 4a42ccee5a
3 changed files with 131 additions and 6 deletions
+19 -2
View File
@@ -119,6 +119,7 @@ import DocumentPreviewModal from '../components/documents/DocumentPreviewModal.v
import { useDocumentsStore } from '../stores/documents.js'
import { useTopicsStore } from '../stores/topics.js'
import * as api from '../api/client.js'
import { fetchDocumentContent } from '../api/client.js'
const route = useRoute()
const router = useRouter()
@@ -157,11 +158,27 @@ onMounted(async () => {
}
})
function openPdf() {
async function openPdf() {
if (pdfOpenMode.value === 'in_app') {
showPreviewModal.value = true
} else {
window.open(api.getDocumentContentUrl(doc.value.id), '_blank')
// Fetch with Authorization header → blob → object URL → window.open
// This closes the unauthenticated access gap: window.open(rawUrl) would bypass
// Bearer auth for cloud documents (plan 05-09 trust boundary).
try {
const res = await fetchDocumentContent(doc.value.id)
if (!res.ok) {
console.error('Failed to open document:', res.status)
return
}
const blob = await res.blob()
const objectUrl = URL.createObjectURL(blob)
window.open(objectUrl, '_blank')
// Revoke after a delay to allow the new tab to load the content
setTimeout(() => URL.revokeObjectURL(objectUrl), 60000)
} catch (err) {
console.error('Failed to open document:', err)
}
}
}