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 | |||
| 14 | 14 | base_url = "https://anvil.localhost" | |
| 15 | 15 | ||
| 16 | 16 | # 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. | |
| 21 | 22 | [oidc] | |
| 22 | 23 | issuer = "https://login.localhost" | |
| 23 | 24 | client_id = "anvil" | |
| ⋯ 22 unchanged lines | |||
modifieddocs/oidc.md+25 −10
| ⋯ 49 unchanged lines | |||
| 50 | 50 | ``` | |
| 51 | 51 | ||
| 52 | 52 | `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. | |
| 58 | 59 | ||
| 59 | 60 | `redirect_uri` must match what is registered at the provider **exactly**; there | |
| 60 | 61 | are no wildcards. It defaults to `base_url` + `/-/oidc/callback`, so getting | |
| ⋯ 10 unchanged lines | |||
| 71 | 72 | --id anvil --name anvil \ | |
| 72 | 73 | --redirect https://anvil.localhost/-/oidc/callback \ | |
| 73 | 74 | --post-logout https://anvil.localhost/ \ | |
| 74 | - | --grant you@example.com:admin | |
| 75 | + | --access-mode open --public | |
| 75 | 76 | ``` | |
| 76 | 77 | ||
| 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: | |
| 79 | 86 | ||
| 80 | 87 | ```sh | |
| 81 | - | ssh collin@hagrid 'docker exec login node --experimental-strip-types \ | |
| 88 | + | ssh hagrid 'docker exec login node --experimental-strip-types \ | |
| 82 | 89 | scripts/register-client.ts --id anvil --name anvil \ | |
| 83 | 90 | --redirect https://anvil.richardscollin.com/-/oidc/callback \ | |
| 84 | 91 | --post-logout https://anvil.richardscollin.com/ \ | |
| 85 | 92 | --grant you@example.com:admin' | |
| 86 | 93 | ``` | |
| 87 | 94 | ||
| 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 | + | ||
| 88 | 100 | Redirect URIs are matched exactly, so development and production need separate | |
| 89 | 101 | 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. | |
| 91 | 106 | ||
| 92 | 107 | ## Local development | |
| 93 | 108 | ||
| ⋯ 50 unchanged lines | |||