fix(pwa): return the caller's own host in the CLI device-flow URLs - #105
Merged
Conversation
`logicsrc login --device` told users to open https://logicsrc-credentials-production.up.railway.app/cli/device even when they had reached the app on the real domain. /cli/device/code built verification_uri from `config.origin`, which is a single fixed value read from $PUBLIC_ORIGIN, so the response was wrong for every hostname except the one that variable happened to name. Derive the origin from the request instead: whatever host the CLI called is the host it gets sent back to. Express honours X-Forwarded-Proto/Host here because server.mjs sets `trust proxy` behind Railway's TLS terminator. Deliberately scoped to the two device-flow URLs. The WebAuthn expectedOrigin in passkey.mjs stays pinned to config.origin — validating a signature against a host the caller supplied would defeat the check. Note this fixes which URL is *printed*; the host still has to route to this service for the link to load. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This was referenced Jul 30, 2026
ralyodio
added a commit
that referenced
this pull request
Jul 30, 2026
…I hint (#108) Two bugs on the dashboard, both fixed by handing appBar/CLI_HINT the request. Sign-out was broken for everyone. csrfGuard rejects any POST whose _csrf does not match the mc_csrf cookie, and /auth/logout is a POST that is not on the exempt list, but the sign-out form carried no hidden field -- every click answered 403 "bad csrf token". appBar now takes the request rather than the user, because it needs the token as well as the identity. The field is written out instead of reusing csrfInput(): html.mjs is the view layer and imports nothing, and pulling in session.mjs would drag the database driver with it. The "Connect the CLI" snippet still printed $PUBLIC_ORIGIN, so users on app.logicsrc.com were told to point LOGICSRC_API at the generated Railway hostname. #105 added requestOrigin() for exactly this and fixed the device-flow URLs; the dashboard hint was missed. It now follows the request too, which is not a hardcode swap -- the same deployment answering on its Railway hostname still self-describes correctly. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
logicsrc login --deviceprinted this, even on the real domain:Root cause
The URL never came from the CLI. The CLI just prints what the server sends:
And the server built it from a single fixed value:
$PUBLIC_ORIGINnames exactly one hostname, so the response is wrong for everyother host the app answers on. (This is also why #104 — which changed the CLI's
DEFAULT_API_URL— had no effect on the printed URL: wrong layer.)Fix
Derive the origin from the request, so whatever host the CLI called is the host it
gets sent back to.
trust proxyis already set inserver.mjs, soX-Forwarded-Proto/X-Forwarded-Hostare honoured behind Railway's TLS terminator.Scoped to the two device-flow URLs on purpose.
expectedOrigininpasskey.mjsstays pinned to
config.origin— validating a WebAuthn signature against acaller-supplied host would defeat the check.
Tests
New
apps/pwa/test/origin.test.mjs, 5 cases, all passing — including that theRailway hostname still self-describes correctly when that is the host, so this
follows the request rather than swapping one hardcode for another.
Still required separately
This fixes which URL is printed. The link only loads once that host routes to
this service — currently
logicsrc.com/cli/devicereturns 404.🤖 Generated with Claude Code