From c5f92d6daa24c563378242ce321e8e9f1518dc37 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Wed, 29 Apr 2026 17:12:58 +0200 Subject: [PATCH] fix(auth): magic link points at /verify.html SPA, not /auth/verify JSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The link in the magic-link email landed users on the JSON API endpoint /auth/verify, exposing the raw response body in the browser. The SPA already ships the wrapper page (verify.html + verify.js) — it reads the token, POSTs to /auth/verify itself, saves the JWT, and redirects to /app.html. Pointing the email at /verify.html restores the intended flow: operator clicks link → verifying… → 'Signed in as ' → 'Go to your notes'. Verified locally: the LogMailer now emits http://localhost:18080/verify.html?token=…, GET /verify.html returns the SPA HTML, and POST /auth/verify still returns the JWT JSON (unchanged). --- internal/auth/service.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/auth/service.go b/internal/auth/service.go index ad0d3b8..e642a43 100644 --- a/internal/auth/service.go +++ b/internal/auth/service.go @@ -99,7 +99,12 @@ func (s *Service) RequestLogin(ctx context.Context, email string) error { if err != nil { return err } - link := s.baseURL + "/auth/verify?token=" + url.QueryEscape(plaintext) + // The magic link must land on the SPA page (verify.html), not the + // JSON API endpoint /auth/verify. The page reads the token from + // the URL, POSTs it to /auth/verify, stores the JWT and redirects + // to /app.html. Pointing the email at /auth/verify exposes the + // raw JSON body to anyone who clicks the link. + link := s.baseURL + "/verify.html?token=" + url.QueryEscape(plaintext) if err := s.mailer.SendMagicLink(ctx, email, link); err != nil { return fmt.Errorf("send mail: %w", err) }