From 590125073c8870649e322a82f81199282e747e18 Mon Sep 17 00:00:00 2001 From: CCS Admin Date: Tue, 24 Feb 2026 09:20:14 +0000 Subject: [PATCH] fix: support token query param for file download endpoints Browser-initiated downloads (window.open) cannot set Authorization headers. Accept ?token= query parameter as fallback on the report download and coding-template endpoints. Co-Authored-By: Claude Opus 4.6 --- backend/app/api/cases.py | 25 ++++++++++++++++++++++--- backend/app/api/reports.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/backend/app/api/cases.py b/backend/app/api/cases.py index 607f31f..14e1208 100644 --- a/backend/app/api/cases.py +++ b/backend/app/api/cases.py @@ -100,16 +100,35 @@ def list_pending_coding( @router.get("/coding-template") def download_coding_template( + request: Request, + token: Optional[str] = Query(None), jahr: Optional[int] = Query(None), fallgruppe: Optional[str] = Query(None), db: Session = Depends(get_db), - user: User = Depends(get_current_user), ): """Download an Excel template for ICD coding. - The template contains all cases that still need ICD codes, with an - empty ICD column for DAK users to fill in. + Supports both ``Authorization: Bearer`` header and ``?token=`` query + parameter for direct browser downloads. """ + from app.core.security import decode_access_token + from jose import JWTError + + raw_token = token + if not raw_token: + auth = request.headers.get("authorization", "") + if auth.lower().startswith("bearer "): + raw_token = auth[7:] + if not raw_token: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Not authenticated") + try: + payload = decode_access_token(raw_token) + user_id = int(payload["sub"]) + except (JWTError, KeyError, ValueError): + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid token") + user = db.query(User).filter(User.id == user_id, User.is_active == True).first() # noqa: E712 + if not user: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "User not found") xlsx_bytes = generate_coding_template(db, jahr=jahr, fallgruppe=fallgruppe) from io import BytesIO diff --git a/backend/app/api/reports.py b/backend/app/api/reports.py index c5e2637..b1abb9e 100644 --- a/backend/app/api/reports.py +++ b/backend/app/api/reports.py @@ -5,7 +5,7 @@ import os from datetime import date from io import BytesIO -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, HTTPException, Query, Request, status from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session @@ -141,13 +141,37 @@ def generate_report( @router.get("/download/{report_id}") def download_report( report_id: int, + request: Request, + token: str | None = Query(None), db: Session = Depends(get_db), - user: User = Depends(get_current_user), ): """Download a previously generated Berichtswesen Excel file. - Accessible to both admin and dak_mitarbeiter users. + Supports both ``Authorization: Bearer`` header and ``?token=`` query + parameter so the browser can open the URL directly in a new tab. """ + from app.core.security import decode_access_token + from jose import JWTError + + # Resolve token from header or query param + raw_token = token + if not raw_token: + auth = request.headers.get("authorization", "") + if auth.lower().startswith("bearer "): + raw_token = auth[7:] + + if not raw_token: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Not authenticated") + + try: + payload = decode_access_token(raw_token) + user_id = int(payload["sub"]) + except (JWTError, KeyError, ValueError): + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid token") + + user = db.query(User).filter(User.id == user_id, User.is_active == True).first() # noqa: E712 + if not user: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "User not found") report = db.query(WeeklyReport).filter(WeeklyReport.id == report_id).first() if not report or not report.report_file_path: raise HTTPException(404, "Report not found")