[fix] stop leaking the Go package prefix into the UI - #151
Merged
Conversation
Every error here carries one — "auth: this account already has the maximum number of authenticators" — which is right for a log and wrong on a screen. It reached one as "auth: passkeys need HTTPS (or localhost)", under the greyed-out Add a passkey button, and I only saw it because it was in a screenshot going into the manual. Stripped in writeErr, so every handler gets it without remembering to, by an allowlist of our own prefixes — "everything before the first colon" would eat half of "cannot reach host: connection refused". Also adds the three screenshots the 1.6.0 work had none of: the sign-in screen, the second step offering a passkey beside the code, and Profile → Security with sessions, both kinds of factor and the passwordless switch. The generator grew a /profile shot and a logged-out pass; its login goto no longer swallows a refused connection, which had silently produced a blank PNG.
After a wrong password the page stays on the sign-in form, which the second-step check read as "this instance signed in without a second factor" — blaming the instance for a wrong DC_PASS. It now says the credentials were refused.
The second-step shot submitted the sign-in form BEFORE the API login, so every run spent two attempts. The server allows five per fifteen minutes, so two runs with a typo locked the operator out of their own instance — which is a poor thing for a screenshot tool to do. The form is now driven only after the API login has confirmed the credentials, where it costs a successful login and resets the budget instead. A 429 says how to clear it: restart, the limiter is in memory.
The committed set was shot by hand at 2560x1353; the generator produces 2560x1440. Re-shot through the generator so the manual is finally consistent, and so the pages that changed in 1.6.0 show what they now do. Profile → Security keeps the demo-instance shot deliberately: it shows an authenticator app and a passkey side by side with passwordless sign-in turned on, which is what the page is documenting. A shot from a real instance shows whatever that operator happens to have paired, plus their live session addresses. The generator gained the five agendas it could not reach: the three Settings tabs, the network graph view and the new-project dialog.
Settings' three tabs, the network graph view and the new-project dialog were the last ones still at the hand-shot 2560x1353. The whole set is now one resolution. Checked before committing: the Email and LDAP tabs are unconfigured on the instance they came from, so they show placeholder hosts rather than anyone's real mail relay or directory.
# Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two things, both found by taking screenshots of the 1.6.0 authentication work.
Error messages were reaching the UI with their Go package prefix. Every error in this codebase carries one —
auth: this account already has the maximum number of authenticators— which is right for a log line and wrong on a screen. It showed up asauth: passkeys need HTTPS (or localhost)under the greyed-out Add a passkey button, and I only noticed because it was in a picture about to be committed to the manual.Stripped in
writeErr, so every handler gets it without having to remember, and on/auth/webauthn/support'sreasonfield. By an allowlist of this app's own prefixes rather than "everything before the first colon" — messages legitimately contain colons, and eating half ofcannot reach host: connection refusedwould be worse than the prefix.Three screenshots the 1.6.0 work had none of. Everything shipped in #141–#148 lives on
/profileand the sign-in screen, and the generator visited neither.login.png— the sign-in screen with the passkey option beside the password.login_2fa.png— the second step, offering the code box and a passkey.profile_security.png— sessions, both kinds of factor, and the passwordless switch.Referenced from
docs/getting-started.mdanddocs/users.md.Generator changes
/profileshot that opens the Security tab.gotono longer swallows errors. It had a.catch(() => {}), which on a refused connection screenshotted a blank page and reported✓ login. That is how the first attempt at this produced an empty PNG.Type of change
Checklist
go test -short ./...andgo vet ./...passgofmtgate is cleanweb/dist— nothing underweb/srcchangeddocs/andCHANGELOG.mdNotes for reviewers
New
internal/api/respond_test.gocovers both the allowlist behaviour and the end-to-end strip; mutation-verified by revertingwriteErrto pass the message through.The screenshots were taken against a throwaway instance on a temp data dir with seeded demo accounts, not against a real deployment — the Security tab shows live session IPs and user agents, and those should not be in a public manual. The Docker-derived pages are untouched here; re-shooting those still needs an instance with real data.
Worth knowing for whoever re-runs the generator: the shots must be taken over
http://localhost:…, nothttp://127.0.0.1:…. An IP literal cannot be a WebAuthn relying party, so every passkey control correctly renders as unavailable — which is exactly what the first run produced.