|
|
1 ay önce | |
|---|---|---|
| .. | ||
| README.md | 1 ay önce | |
| config-sync.json | 1 ay önce | |
Provisions the demo scope, user, entitlement, and redirect URI against a local
d-OAuth authorization server, then registers this workspace's confidential BFF
client over Dynamic Client Registration (DCR). No real secrets appear below:
passwords travel via --password-file, the IAT is printed once by the issue
step and handed to DCR, and nothing secret is committed.
Conventions used throughout: demo scope demo.read, demo user
demo@example.com, redirect URI http://127.0.0.1:3000/callback. The binary
is d-oauth-server (cargo run --bin d-oauth-server -- <command> from the
d-OAuth checkout). Flag names below are taken from d-OAuth
docs/OPERATIONS.md (command reference).
# From the d-OAuth checkout: migrate once, then serve.
d-oauth-server migrate
D_OAUTH_ISSUER=https://localhost:8080 d-oauth-server serve
serve runs the TLS public listener on 127.0.0.1:8080 (self-signed dev
certificate) plus the plaintext health listener on 127.0.0.1:8081. It never
migrates and never provisions. Readiness:
curl -fsS http://127.0.0.1:8081/health/ready
d-oauth-server scope create demo.read --description "Demo read scope"
d-oauth-server scope enable demo.read
scope create upserts (create-or-update-description) and enable flips the
catalog flag. Stale-discovery note: if the AS was already running when the
scope was created, /metadata scopes_supported may be stale (in-process
cache) until restart. The OAuth flow reads Postgres directly, so either
restart the AS after scope creation or do not assert scopes from discovery.
printf '%s' '<initial-password>' > /tmp/demo-password.txt
d-oauth-server user create --email demo@example.com --password-file /tmp/demo-password.txt
d-oauth-server user grant-scope --email demo@example.com --scope demo.read
d-oauth-server user scopes --email demo@example.com
rm /tmp/demo-password.txt
user create is a plain INSERT, not an upsert: re-running it against an
existing user fails with an already-exists error (the provision setup step
treats that as create-or-skip). grant-scope requires the scope to exist in
the catalog (step 1 first) and upserts. A created user starts with
must_change_password = true — see step 6.
Declarative alternative (idempotent; second run reports no changes):
d-oauth-server config sync --file demo/config-sync.json
d-oauth-server config sync --file demo/config-sync.json
demo/config-sync.json covers scopes, redirect URIs, and entitlements. It
CANNOT create clients: the sync schema has no clients section, so the
confidential client always comes from step 5 (DCR via provision register).
d-oauth-server redirect-uri create http://127.0.0.1:3000/callback
The URI is a global catalog entry. After DCR mints the client id, attach it:
d-oauth-server client attach-redirect-uri --client-id <client-id> --uri http://127.0.0.1:3000/callback
(client_redirect_uris in demo/config-sync.json is left empty because the
client id only exists after step 5.)
d-oauth-server initial-access-token issue --uses 1 --ttl 600
The raw token prints once; only its keyed hash is stored. It is required when
allow_open_registration is false. A burned IAT (even one spent on a failed
registration) is never reused — issue a fresh one per attempt. Prefer the
automated path instead of copy-paste:
cargo run -p provision -- setup --cli <path-to-d-oauth-server> --env-file <d-OAuth-.env> \
--email demo@example.com --password-file <path> --iat-out /tmp/demo-iat.txt
cargo run -p provision -- register --issuer https://localhost:8080 --iat-file /tmp/demo-iat.txt
rm /tmp/demo-iat.txt
provision register)provision register POSTs /register with
token_endpoint_auth_method=client_secret_basic,
grant_types=[authorization_code,refresh_token], response_types=[code],
the demo redirect URI/scope, and dpop_bound_access_tokens=true, then
persists config/client.json (mode 0600).
Catalog-policy remediation: under the default dcr_redirect_policy=catalog,
only redirect URIs present in the runtime catalog may register. An
invalid_redirect_uri rejection means step 3 was skipped: provision the URI
first, or relax the AS to dcr_redirect_policy=catalog_loopback (catalog
URIs plus dynamic loopback URIs, RFC 8252) — never open in production.
The fresh demo user cannot reach /authorize until both complete (restricted
session). The automated path is provision bootstrap + the Playwright spec
(todo 37), which sets a new known password, captures the otpauth:// secret
from /login/totp/setup, and persists {email, password, totp_secret} to
config/e2e.json for reuse. Manual equivalent:
POST /login (email, password, hidden csrf; code empty) → sent to
/login/change-password.POST /login/change-password (new_password of at least 12 characters,
confirm, hidden csrf).GET /login/totp/setup → register the shown secret/otpauth:// URI in
an authenticator now (shown once, with one-time recovery codes).POST /login/totp/confirm (code, hidden csrf) → full session.POST /consent approves with hidden request_id, hidden csrf, and
decision=approve.# BFF (confidential client): http://127.0.0.1:3001, health at /healthz.
cargo run -p server
curl -fsS http://127.0.0.1:3001/healthz
Dev UI (Trunk owns :3000, proxies /oauth/, /callback, /api/ to the
BFF on :3001):
cd crates/web && trunk serve --port 3000
Browser entry point: http://127.0.0.1:3000. Approve with decision=approve
at /consent; the flow lands back at /callback on the BFF.
Log out in the dashboard clears the BFF session, then navigates the browser to
the provider logout (<issuer>/logout). d-OAuth ends its own session and 302s
back to the app root (http://127.0.0.1:3000/, the client's registered
post-logout return URI), so a different account can sign in on the next run
without restarting the authorization server.
The scope demo.read is required. If the account authenticates without it, the
dashboard keeps the session and shows a Signed in without the required scope
notice; if the AS refuses the scope at authorization time, /callback renders a
Scope not granted page. Grant the scope (step 2), then sign in again.