The link inside the magic-link email pointed at /auth/verify, the JSON API endpoint, so anyone clicking the link saw the raw {"jwt":"…","user_id":"…","email":"…"} body in the browser. The frontend already ships the wrapper page (web/public/verify.html + verify.js) that reads ?token= from the URL, POSTs /auth/verify, stores the JWT via authClient.saveSession(), and redirects to /app.html. Backend just built the wrong URL.
One-line change in internal/auth/service.go plus a longer comment explaining why. Verified locally with the dev binary:
$ tail -1 ln.log
magic link for test@example.com: http://localhost:18080/verify.html?token=…
$ curl /verify.html?token=… → 200 text/html (the SPA wrapper)
$ curl -X POST /auth/verify?token=… → 200 application/json (jwt + user_id + email)
Existing unit tests still pass (they parse the token from the email body via extractToken, which finds token= regardless of the path prefix). Reported by the operator after seeing the JSON page on first click in production at https://ln.cloud.librete.ch/.
The link inside the magic-link email pointed at `/auth/verify`, the JSON API endpoint, so anyone clicking the link saw the raw `{"jwt":"…","user_id":"…","email":"…"}` body in the browser. The frontend already ships the wrapper page (`web/public/verify.html` + `verify.js`) that reads `?token=` from the URL, POSTs `/auth/verify`, stores the JWT via `authClient.saveSession()`, and redirects to `/app.html`. Backend just built the wrong URL.
One-line change in `internal/auth/service.go` plus a longer comment explaining why. Verified locally with the dev binary:
```
$ tail -1 ln.log
magic link for test@example.com: http://localhost:18080/verify.html?token=…
$ curl /verify.html?token=… → 200 text/html (the SPA wrapper)
$ curl -X POST /auth/verify?token=… → 200 application/json (jwt + user_id + email)
```
Existing unit tests still pass (they parse the token from the email body via `extractToken`, which finds `token=` regardless of the path prefix). Reported by the operator after seeing the JSON page on first click in production at `https://ln.cloud.librete.ch/`.
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 <email>' →
'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).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The link inside the magic-link email pointed at
/auth/verify, the JSON API endpoint, so anyone clicking the link saw the raw{"jwt":"…","user_id":"…","email":"…"}body in the browser. The frontend already ships the wrapper page (web/public/verify.html+verify.js) that reads?token=from the URL, POSTs/auth/verify, stores the JWT viaauthClient.saveSession(), and redirects to/app.html. Backend just built the wrong URL.One-line change in
internal/auth/service.goplus a longer comment explaining why. Verified locally with the dev binary:Existing unit tests still pass (they parse the token from the email body via
extractToken, which findstoken=regardless of the path prefix). Reported by the operator after seeing the JSON page on first click in production athttps://ln.cloud.librete.ch/.