Wire Kratos self-service flows to themed routes (todo §3); enable recovery/verification via email code, add mailpit dev courier + --watch-courier
This commit is contained in:
@@ -113,8 +113,9 @@ docker compose up # http://localhost:3000, live reload via `node --wa
|
||||
|
||||
`docker compose up` merges `compose.override.yml`, which mounts the source and
|
||||
restarts the server on change. _(The Ory + Postgres services join this compose
|
||||
file as they land — planned.)_ To work on your own plugin, see
|
||||
[Where plugins live](#where-plugins-live-and-how-to-mount-them).
|
||||
file as they land — planned.)_ Kratos recovery/verification emails are caught by
|
||||
**mailpit** in dev — read the codes at http://localhost:8025. To work on your own
|
||||
plugin, see [Where plugins live](#where-plugins-live-and-how-to-mount-them).
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -10,3 +10,11 @@ services:
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
|
||||
# Dev mail catcher — Kratos recovery/verification emails land here (web UI on 8025).
|
||||
# kratos.yml points the courier at smtp://mailpit:1025; prod uses a real SMTP via env.
|
||||
mailpit:
|
||||
image: axllent/mailpit:v1.30.1
|
||||
ports:
|
||||
- "8025:8025"
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -54,7 +54,7 @@ services:
|
||||
DSN: postgres://${POSTGRES_USER:-ory}:${POSTGRES_PASSWORD:-ory}@postgres:5432/kratos?sslmode=disable
|
||||
volumes:
|
||||
- ./ory/kratos:/etc/config/kratos:ro
|
||||
command: serve -c /etc/config/kratos/kratos.yml
|
||||
command: serve -c /etc/config/kratos/kratos.yml --watch-courier
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Ory Kratos — identity & self-service auth. Bootable baseline (§3): identity
|
||||
# schema (email, name) + password login. DSN comes from the env (compose), so it
|
||||
# is absent here. Self-service UIs point at the web app's routes; theming those
|
||||
# pages, SSO, session tuning, and the JWT tokenizer land in later §3/§4 items.
|
||||
# Ory Kratos — identity & self-service auth. Identity schema (email, name) +
|
||||
# password login; recovery & verification run on email codes. Every self-service
|
||||
# flow returns the browser to our own themed routes (§4 renders the fields). DSN +
|
||||
# prod courier/secrets come from the env. SSO, session tuning, and the JWT
|
||||
# tokenizer land in later §3/§4 items.
|
||||
serve:
|
||||
public:
|
||||
base_url: http://127.0.0.1:4433/
|
||||
@@ -17,6 +18,8 @@ selfservice:
|
||||
methods:
|
||||
password:
|
||||
enabled: true
|
||||
code: # email one-time code — powers recovery + verification (not login)
|
||||
enabled: true
|
||||
flows:
|
||||
error:
|
||||
ui_url: http://127.0.0.1:3000/error
|
||||
@@ -24,12 +27,36 @@ selfservice:
|
||||
ui_url: http://127.0.0.1:3000/login
|
||||
registration:
|
||||
ui_url: http://127.0.0.1:3000/registration
|
||||
after:
|
||||
password:
|
||||
hooks:
|
||||
- hook: session # log in immediately after sign-up
|
||||
- hook: show_verification_ui
|
||||
settings:
|
||||
ui_url: http://127.0.0.1:3000/settings
|
||||
privileged_session_max_age: 15m
|
||||
required_aal: highest_available
|
||||
recovery:
|
||||
enabled: true
|
||||
use: code
|
||||
ui_url: http://127.0.0.1:3000/recovery
|
||||
verification:
|
||||
enabled: true
|
||||
use: code
|
||||
ui_url: http://127.0.0.1:3000/verification
|
||||
after:
|
||||
default_browser_return_url: http://127.0.0.1:3000/
|
||||
logout:
|
||||
after:
|
||||
default_browser_return_url: http://127.0.0.1:3000/login
|
||||
|
||||
# Dev mail catcher (compose.override.yml). Prod overrides via COURIER_SMTP_CONNECTION_URI.
|
||||
courier:
|
||||
smtp:
|
||||
connection_uri: smtp://mailpit:1025/?disable_starttls=true
|
||||
from_address: no-reply@plainpages.local
|
||||
from_name: Plainpages
|
||||
|
||||
identity:
|
||||
default_schema_id: default
|
||||
schemas:
|
||||
|
||||
@@ -45,3 +45,26 @@ test("kratos config wires the identity schema", () => {
|
||||
assert.match(kratosYml, /default_schema_id:\s*default/);
|
||||
assert.match(kratosYml, /identity\.schema\.json/);
|
||||
});
|
||||
|
||||
// The five self-service flows return the browser to our own themed routes (§4 renders them).
|
||||
const FLOW_PAGES = ["login", "registration", "recovery", "verification", "settings"];
|
||||
|
||||
test("self-service flows return to our themed pages", () => {
|
||||
for (const flow of FLOW_PAGES)
|
||||
assert.match(kratosYml, new RegExp(`ui_url:\\s*http://127\\.0\\.0\\.1:3000/${flow}\\b`),
|
||||
`${flow} flow points at our /${flow} page`);
|
||||
});
|
||||
|
||||
test("recovery + verification run on email code, delivered by a courier", () => {
|
||||
assert.ok((kratosYml.match(/use:\s*code/g) ?? []).length >= 2,
|
||||
"recovery + verification both use the email-code method");
|
||||
assert.match(kratosYml, /connection_uri:\s*smtp:\/\/mailpit:1025/, "courier sends via the dev mail catcher");
|
||||
assert.match(compose, /--watch-courier/, "kratos dispatches queued mail (else codes never send)");
|
||||
});
|
||||
|
||||
test("compose pins the dev mail catcher to an exact version", () => {
|
||||
const tag = read("compose.override.yml").match(/image:\s*axllent\/mailpit:(\S+)/)?.[1];
|
||||
assert.ok(tag, "compose.override.yml pins a mailpit image");
|
||||
assert.match(tag, /^v\d+\.\d+\.\d+$/, `${tag} is an exact version`);
|
||||
assert.doesNotMatch(tag, /latest|edge|[\^~*]/, `${tag} is exact, not floating`);
|
||||
});
|
||||
|
||||
3
todo.md
3
todo.md
@@ -59,7 +59,7 @@ everything via Docker.
|
||||
## 3. Ory stack — compose + config
|
||||
- [x] `postgres` service (pinned tag); separate DB/schema per Kratos/Keto/Hydra. → `compose.yml` `postgres` service pinned to `postgres:18.4-alpine3.23` (verified latest stable PG + newest Alpine the official image ships); `ory/postgres/init/init.sql` (mounted at `docker-entrypoint-initdb.d`) creates one DB per service (`kratos`/`keto`/`hydra`) so each owns its schema + migrations. Dev defaults (`ory`/`ory`, env-overridable for prod), named `pgdata` volume mounted at `/var/lib/postgresql` (PG18+ version-subdir layout — not `/data`), `pg_isready` healthcheck. Web app never connects. Verified live: boots healthy, three DBs present, then torn down. `postgres.test.ts` guards the pin + DB-per-service. typecheck + 112 units green.
|
||||
- [x] `kratos` service (pinned) + `migrate`; identity schema (traits: email, name). → `compose.yml` adds `kratos`/`kratos-migrate` pinned to `oryd/kratos:v26.2.0` (verified latest stable); `kratos-migrate` runs `migrate sql -e --yes` against the per-service `kratos` DB after postgres is healthy, `kratos` waits for it (`service_completed_successfully`). `ory/kratos/identity.schema.json` = email (password identifier, verification/recovery via email) + `name {first,last}`, email required. `ory/kratos/kratos.yml` = bootable baseline: password login, self-service UIs pointing at the web routes (themed in §4), serve URLs, dev-throwaway secrets (prod via env, §3), identity schema wired; DSN via env. Themed flows/SSO/session/tokenizer/JWKS are the next §3/§4 items. Tests-first (`kratos.test.ts`: version pin + migrate-before-serve + DSN→kratos DB + schema traits + schema wiring). Boot-verified: migrate exits 0, kratos serves `/health/ready` 200, serves the identity schema, inits a password login flow; torn down. typecheck + 117 units green.
|
||||
- [ ] Kratos self-service flows (login, registration, recovery, verification, settings) → return URLs at our themed pages.
|
||||
- [x] Kratos self-service flows (login, registration, recovery, verification, settings) → return URLs at our themed pages. → `ory/kratos/kratos.yml`: all five flows enabled, each `ui_url` (+ after/return URLs) points at our web routes (`/login`, `/registration`, `/recovery`, `/verification`, `/settings`; §4 renders the fields). Recovery + verification run on the email `code` method (login stays password-only — `code.passwordless_enabled` left default-off); registration after-hooks `session` + `show_verification_ui`; settings gets `privileged_session_max_age` + `required_aal: highest_available`. Added a `courier` (SMTP) sending to a pinned dev mail catcher — **mailpit** (`axllent/mailpit:v1.30.1`) in `compose.override.yml`, web UI on `:8025`; prod overrides `COURIER_SMTP_CONNECTION_URI`. Kratos `serve` now runs `--watch-courier` so queued codes actually dispatch (without it they sit "queued"). Tests-first (`kratos.test.ts`: five flow ui_urls → our pages, recovery/verification use `code` + courier + `--watch-courier`, mailpit pin). Boot-verified end-to-end: all four public browser-flows 303 → `127.0.0.1:3000/<flow>?flow=…`, a registration delivered a real "Use code … to verify your account" email to mailpit (queue → `sent`); torn down. typecheck + 120 units green.
|
||||
- [ ] Kratos OIDC/SSO providers (Google/Microsoft/SAML) config (secrets via env). **None enabled by default** — a clean clone runs password-only; a provider activates purely by supplying its env creds.
|
||||
- [ ] Kratos session settings (cookie name, lifespan, sliding refresh).
|
||||
- [ ] Kratos tokenizer template `plainpages`: claims `{ sub, email, roles }`, `ttl ≈ 10m`, `jwks_url` signer, `claims_mapper_url` (Jsonnet reading `metadata_admin.roles`).
|
||||
@@ -136,5 +136,4 @@ everything via Docker.
|
||||
- [ ] Go over all tests and combine/unify ones that cover the same stuff or are very related and could be combined in a good way. Remove tests that aren't helping, we only want tests that are actually helpful to us.
|
||||
|
||||
## 10. User added stuff
|
||||
- [ ] If no seeded default user is already added, add it so you get something to work with on a new installation. Should be a default initial account name and password in ENVs or a better suggestion.
|
||||
- [ ] Make some pages optionally available publicly.
|
||||
Reference in New Issue
Block a user