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:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user