fix(frontend): Auth-Header-Weiterleitung an Backend behebt Dashboard 503#95
Conversation
Nach dem Merge von Phase 7c (PR #88) erfordert das Backend eine Authentifizierung via X-Label-Hub-Key, X-Pangolin-User oder Authorization-Header. Der HubClient sendete diese Headers beim internen Docker-Netzwerk-Call nicht mit, da kein Pangolin-Proxy zwischen Frontend und Backend sitzt. Folge: Backend antwortete mit 401 → JSON200 == nil → Dashboard lieferte 503 Service Unavailable. Fix: HubClient.WithAuthFrom(*http.Request) kopiert die drei Auth-Header aus dem eingehenden Browser-Request in alle ausgehenden Backend-Calls via RequestEditorFn. Alle Page-Handler (Dashboard, Printer, Jobs, Templates, Lookup) wurden entsprechend angepasst. forwardAuth() in admin_api_keys.go bleibt als separater Mechanismus für die Raw-HTTP-Calls an /api/admin/api-keys/* erhalten. Reproduktion: docker exec frontend wget -qO- http://backend:8000/api/printers → 401 ohne Header, 200 mit X-Pangolin-User.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #95 +/- ##
==========================================
+ Coverage 89.84% 89.87% +0.02%
==========================================
Files 89 89
Lines 4008 4008
Branches 343 343
==========================================
+ Hits 3601 3602 +1
Misses 317 317
+ Partials 90 89 -1
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical authentication issue introduced in a previous phase where internal frontend-to-backend requests were failing due to missing authentication headers. By modifying the 'HubClient' to inject these headers from the original user request, the system now correctly authenticates internal calls, restoring full functionality to the dashboard and other dependent services. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Pull request overview
Fixes the frontend dashboard (and other pages) returning 503 after Phase 7c backend auth by forwarding incoming auth headers on all frontend→backend API calls made through the typed HubClient.
Changes:
- Added
HubClient.WithAuthFrom(*http.Request)to forwardX-Label-Hub-Key,X-Pangolin-User, andAuthorizationviaRequestEditorFnon generated API calls. - Updated all affected page handlers to call backend APIs through
h.client.WithAuthFrom(r)(or a sharedauthClient). - Added regression tests ensuring 401 handling and header forwarding behavior.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| frontend/internal/handlers/templates.go | Uses auth-scoped client for template listing. |
| frontend/internal/handlers/template.go | Uses auth-scoped client for template list fetch and preview rendering call path. |
| frontend/internal/handlers/printer.go | Creates authClient once and uses it for parallel backend calls. |
| frontend/internal/handlers/lookup.go | Uses auth-scoped client for lookup calls. |
| frontend/internal/handlers/jobs.go | Uses auth-scoped client for job listing calls. |
| frontend/internal/handlers/job.go | Uses auth-scoped client for job detail and retry calls. |
| frontend/internal/handlers/dashboard.go | Uses auth-scoped client for printer listing on dashboard. |
| frontend/internal/api/client.go | Adds auth header forwarding support and applies request editors to generated client calls. |
| frontend/internal/api/client_test.go | Adds regression tests for 401 behavior and auth header forwarding. |
| // WithAuthFrom returns a new HubClient that copies auth-related headers from | ||
| // the incoming browser request into every outgoing backend call. | ||
| // | ||
| // The headers forwarded are: | ||
| // - X-Label-Hub-Key — API-key auth (Phase 7c) |
There was a problem hiding this comment.
Code Review
This pull request implements authentication header forwarding in the frontend's HubClient using a new WithAuthFrom method to propagate X-Label-Hub-Key, X-Pangolin-User, and Authorization headers to backend calls. The review feedback identifies two critical issues: first, RenderPreview bypasses the generated client and does not apply the auth headers, causing preview generation to fail under authentication; second, WithAuthFrom lacks a nil-check for the incoming request r, which can lead to a nil-pointer dereference panic.
| previewCtx, previewCancel := context.WithTimeout(r.Context(), 2*time.Second) | ||
| defer previewCancel() | ||
| previewBytes, previewErr := h.client.RenderPreview(previewCtx, key) | ||
| previewBytes, previewErr := h.client.WithAuthFrom(r).RenderPreview(previewCtx, key) |
There was a problem hiding this comment.
Obwohl hier WithAuthFrom(r) aufgerufen wird, werden die Authentifizierungs-Header bei RenderPreview nicht an das Backend weitergeleitet. Die Methode RenderPreview in frontend/internal/api/client.go verwendet c.hc.Do(req) direkt und wendet weder c.editors() noch die c.authHeaders auf den ausgehenden Request an. Dies führt dazu, dass die Vorschau-Generierung fehlschlägt (401 Unauthorized), wenn das Backend eine Authentifizierung erfordert.
Empfohlene Behebung in frontend/internal/api/client.go:
func (c *HubClient) RenderPreview(ctx context.Context, templateKey string) ([]byte, error) {
start := time.Now()
previewURL := c.baseURL + "/api/render/preview?key=" + url.QueryEscape(templateKey)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, previewURL, nil)
if err != nil {
return nil, err
}
// Auth-Header manuell hinzufügen:
for k, v := range c.authHeaders {
req.Header.Set(k, v)
}
resp, err := c.hc.Do(req)
// ...
}| func (c *HubClient) WithAuthFrom(r *http.Request) *HubClient { | ||
| headers := make(map[string]string, 3) | ||
| for _, h := range []string{"X-Label-Hub-Key", "X-Pangolin-User", "Authorization"} { | ||
| if v := r.Header.Get(h); v != "" { | ||
| headers[h] = v | ||
| } | ||
| } | ||
| return &HubClient{ | ||
| gen: c.gen, | ||
| hc: c.hc, | ||
| baseURL: c.baseURL, | ||
| authHeaders: headers, | ||
| } | ||
| } |
There was a problem hiding this comment.
Wenn r nil ist (beispielsweise in bestimmten Testfällen oder Hintergrund-Tasks), führt der Aufruf von r.Header.Get(h) zu einer Nil-Pointer-Dereferenzierung und damit zu einem Absturz (Panic). Es sollte eine defensive Überprüfung hinzugefügt werden, um dies zu verhindern.
| func (c *HubClient) WithAuthFrom(r *http.Request) *HubClient { | |
| headers := make(map[string]string, 3) | |
| for _, h := range []string{"X-Label-Hub-Key", "X-Pangolin-User", "Authorization"} { | |
| if v := r.Header.Get(h); v != "" { | |
| headers[h] = v | |
| } | |
| } | |
| return &HubClient{ | |
| gen: c.gen, | |
| hc: c.hc, | |
| baseURL: c.baseURL, | |
| authHeaders: headers, | |
| } | |
| } | |
| func (c *HubClient) WithAuthFrom(r *http.Request) *HubClient { | |
| if r == nil { | |
| return c | |
| } | |
| headers := make(map[string]string, 3) | |
| for _, h := range []string{"X-Label-Hub-Key", "X-Pangolin-User", "Authorization"} { | |
| if v := r.Header.Get(h); v != "" { | |
| headers[h] = v | |
| } | |
| } | |
| return &HubClient{ | |
| gen: c.gen, | |
| hc: c.hc, | |
| baseURL: c.baseURL, | |
| authHeaders: headers, | |
| } | |
| } |
…n) als SSO-Pfad (#96) Backend akzeptiert Pangolin-Standard-Header (Remote-User + X-Pangolin-Token Trust-Validation) analog zu Hangar. Legacy X-Pangolin-User bleibt für Frontend-internal-Calls (PR #95) erhalten. PRINTER_HUB_SSO_TRUST_TOKEN muss in Backend-Env gesetzt werden, sonst ist der Pfad sicherheitshalber deaktiviert (leerer Default). 6 neue Tests, 876 Tests grün insgesamt.
Symptom
https://labels.strausmann.cloud/liefert seit Phase 7c (PR #88) systematisch503 Service Unavailable./healthzund/api/printersvia Pangolin antworten mit 200.Root Cause (Phase 1 Diagnose)
Phase 7c fügte Backend-Authentifizierung ein (drei Pfade: X-Label-Hub-Key, X-Pangolin-User, Authorization Basic). Browser-Requests kommen über Pangolin → Backend erhält
X-Pangolin-User. AberHubClientbaut einen eigenen internen Docker-Netzwerk-Call auf — ohne Pangolin in der Mitte. Die Auth-Headers fehlen → Backend gibt 401 zurück →resp.JSON200 == nil→dashboard.go:25rendert 503.Die ursprüngliche Hypothese (fehlende
slug-Feld im oapi-codegen führt zu Decode-Error) war falsch: Gojson.Unmarshalignoriert unbekannte Felder, kein Decode-Error. Der echte Crash war immer die 401.Fix
HubClient.WithAuthFrom(r *http.Request)gibt einen neuen*HubClientzurück, der viaRequestEditorFndie drei Auth-Header aus dem eingehenden Browser-Request in jeden ausgehenden Backend-Call einfügt.Alle Page-Handler nutzen nun
h.client.WithAuthFrom(r).MethodXxx(...):ListPrinters)GetPrinterDetail,GetPrinterStatus,GetPrinterTape,GetPrinterQueue)ListTemplates,RenderPreview)LookupEntity)forwardAuth()inadmin_api_keys.gobleibt als separater Mechanismus für die Raw-HTTP-Calls an/api/admin/api-keys/*erhalten — dort gibt es keinHubClient.Tests
3 neue Regressionstests in
frontend/internal/api/client_test.go:TestListPrintersForwards401AsError— 401 vom Backend → ListPrinters gibt Fehler zurückTestWithAuthFromForwardsPangolinHeader— X-Pangolin-User wird an Backend durchgereichtTestWithAuthFromForwardsAPIKeyHeader— X-Label-Hub-Key wird an Backend durchgereichtVerifikation nach Merge
Watchtower deployed das neue
:dev-Image automatisch (Polling ~10 min). Danach: