anvilsign in

collin/anvil · f088bfe3

Register the dev SSO client as public, and document where the secret lives

Collin Richards · 2026-08-18 14:42 UTC · f088bfe3205afcbd75b57c6e099baa0eff1efc1e · parent 21f74794 · browse files

modifieddeploy/anvil.dev.toml+5 −4
⋯ 13 unchanged lines
1414 base_url = "https://anvil.localhost"
1515
1616 # Single sign-on against the local instance of login.richardscollin.com
17-# (../login-richardscollin, `portless` → https://login.localhost). Register the
18-# client there first and pass ANVIL_OIDC_CLIENT_SECRET to deploy/dev.sh — see
19-# docs/oidc.md. Until then the sign-in button is there but the provider is not,
20-# so use a password.
17+# (../login-richardscollin, `portless` → https://login.localhost). The dev
18+# client there is registered as *public* — PKCE only, no secret — so this needs
19+# no credential in the file and none in the environment. See docs/oidc.md.
20+# With that provider not running, the sign-in button is there but the provider
21+# is not, so use a password.
2122 [oidc]
2223 issuer = "https://login.localhost"
2324 client_id = "anvil"
⋯ 22 unchanged lines
modifieddocs/oidc.md+25 −10
⋯ 49 unchanged lines
5050 ```
5151
5252 `ANVIL_OIDC_ISSUER`, `ANVIL_OIDC_CLIENT_ID`, `ANVIL_OIDC_CLIENT_SECRET` and
53-`ANVIL_OIDC_REDIRECT_URI` override the file. **Keep the secret in the
54-environment**: config files get committed, and `deploy/run.sh` (production) and
55-`deploy/dev.sh` (local) both pass `ANVIL_OIDC_CLIENT_SECRET` through when it is
56-set. A client registered as public needs no secret at all — PKCE protects the
57-code either way.
53+`ANVIL_OIDC_REDIRECT_URI` override the file. **Keep the secret out of the
54+config file**: those get committed. `deploy/run.sh` reads it from
55+`~/.config/anvil/oidc-client-secret` on the host (or the environment, which
56+wins), and `deploy/dev.sh` passes `ANVIL_OIDC_CLIENT_SECRET` through when set.
57+A client registered as public needs no secret at all — PKCE protects the code
58+either way, which is how the local dev client is set up.
5859
5960 `redirect_uri` must match what is registered at the provider **exactly**; there
6061 are no wildcards. It defaults to `base_url` + `/-/oidc/callback`, so getting
⋯ 10 unchanged lines
7172 --id anvil --name anvil \
7273 --redirect https://anvil.localhost/-/oidc/callback \
7374 --post-logout https://anvil.localhost/ \
74- --grant you@example.com:admin
75+ --access-mode open --public
7576 ```
7677
77-That prints the client secret once. Against the deployed provider the same
78-script runs inside the container, which is where production's database lives:
78+`--public` is what makes local development frictionless: PKCE only, no secret
79+to carry into `deploy/anvil.dev.toml` or the container's environment. With
80+`--access-mode open`, any account at the local provider can sign in, so there
81+is no grant to keep in step either.
82+
83+Production is the opposite on both counts — a confidential client, and access
84+by grant. The same script runs inside the deployed container, which is where
85+that database lives:
7986
8087 ```sh
81-ssh collin@hagrid 'docker exec login node --experimental-strip-types \
88+ssh hagrid 'docker exec login node --experimental-strip-types \
8289 scripts/register-client.ts --id anvil --name anvil \
8390 --redirect https://anvil.richardscollin.com/-/oidc/callback \
8491 --post-logout https://anvil.richardscollin.com/ \
8592 --grant you@example.com:admin'
8693 ```
8794
95+It prints the secret once. Put it at `~/.config/anvil/oidc-client-secret`
96+(mode 600) on the host, which is where `deploy/run.sh` looks — `deploy.sh`
97+pipes that script over ssh with no environment attached, so a file is the only
98+thing that survives the trip.
99+
88100 Redirect URIs are matched exactly, so development and production need separate
89101 entries (pass `--redirect` twice) or separate clients. Production's client here
90-carries production URIs only.
102+carries production URIs only. Note the provider stores the *normalized* form of
103+what you register (`https://host` becomes `https://host/`), and compares
104+character for character — anvil normalizes its post-logout URI the same way so
105+the two agree.
91106
92107 ## Local development
93108
⋯ 50 unchanged lines