fix(auth): pass Auth.js variables to the container and fix redirect origins - #23
Merged
Merged
Conversation
…ct origins Three faults, found from one screenshot of a failed Google sign-in on production. Sign-in could not have worked with any of them present. docker-compose.yml never passed a single AUTH_* variable to the app service. Compose gives a container nothing that is not named in its environment block, so setting them in the VM's .env would have changed nothing and looked like the values were wrong rather than absent. This is the one that made the outage unfixable from the operator's side. Auth.js built its redirect and error URLs from the request origin, which behind the VM's nginx resolves to the container's bind address -- the Dockerfile sets HOSTNAME=0.0.0.0 and that proxy does not forward Host. Every failed sign-in sent the browser to https://0.0.0.0:3000/signin?error=... and died on ERR_ADDRESS_INVALID, so the one page that could have explained the failure was the one page unreachable. The runner stage now defaults AUTH_URL from the NEXT_PUBLIC_APP_URL build arg, which needs no new variable and no proxy cooperation -- the fix docs/HANDOFF.md already prefers over relying on `proxy_set_header Host $host`. Compose can still override it. The sign-in page rendered ?error= as nothing at all. pages.error points here, so a failed OAuth round trip returns with a code and no other signal, and the page looked exactly like a fresh visit -- indistinguishable from the button being broken. Codes are now mapped to sentences, with two that matter: Configuration says the fault is ours and is not retryable, because telling someone to try again against a misconfigured server is a loop that cannot succeed; and AccessDenied, which is what our own signIn callback returns when it refuses to link an OAuth identity to an existing account, explains the way forward instead of reading as a generic failure. Unknown codes fall back rather than printing the raw code at a user. The provider buttons and the submit are disabled when the error is one retrying cannot clear.
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.
Three faults, all found from one screenshot of a failed Google sign-in on production. Sign-in could not have worked with any of them present.
1. The container never received the Auth.js variables
docker-compose.ymlpassed noAUTH_*variable to theappservice. Compose gives a container nothing that isn't named in itsenvironment:block, so setting them in the VM's.envwould have changed nothing — and would have looked like the values were wrong rather than absent.This is the one that made the outage unfixable from the operator's side. All nine are now passed, with the
AUTH_ADAPTER_SECRETnote about it needing to match the Convex deployment.2. Redirects went to
https://0.0.0.0:3000Auth.js builds redirect and error URLs from the request origin. The Dockerfile sets
HOSTNAME=0.0.0.0, and the VM's nginx does not forward theHostheader, so that origin resolved to the container's bind address. Every failed sign-in sent the browser tohttps://0.0.0.0:3000/signin?error=Configurationand died onERR_ADDRESS_INVALID— so the one page that could have explained the failure was the one page the user could not reach.The runner stage now defaults
AUTH_URLfrom theNEXT_PUBLIC_APP_URLbuild arg. No new variable to set, no proxy cooperation required — the fixdocs/HANDOFF.mdalready prefers over relying onproxy_set_header Host $host. Compose can still override it.3.
?error=rendered as nothingpages.errorpoints at/signin, so a failed OAuth round trip comes back with a code and no other signal — and the page looked exactly like a fresh visit. Indistinguishable from the button being broken.Codes now map to sentences. Two matter:
Configurationsays the fault is ours and marks itself non-retryable, because telling someone to try again against a misconfigured server is a loop that cannot succeed. The provider buttons and submit are disabled for it.AccessDeniedis what our ownsignIncallback returns when it refuses to link an OAuth identity to an existing account. It explains the way forward rather than reading as a generic failure.Unknown codes fall back rather than printing a raw code at a user.
Verification
tsc --noEmitclean, production build green.docker-compose.ymland the workflow YAML both parse.Deploying this
Still needs the values themselves set in the VM's
.env— this PR makes them reach the app, it does not invent them.AUTH_ADAPTER_SECRETmust additionally be set on Convex withnpx convex env set AUTH_ADAPTER_SECRET <same value> --prod, and the OAuth redirect URIs registered:/api/auth/callback/google,/api/auth/callback/github.After this image rolls out,
/.well-known/jwks.jsonreturning a key instead of 503 and/api/healthreporting"auth": trueare the two quickest confirmations.