Repository navigation
Admin sign-in and access #29
Copy link
Copy link
Closed
Labels
ready-for-agentFully specified, ready for an AFK agentFully specified, ready for an AFK agent
Description
Parent
What to build
An admin can sign in to the admin pages, on their own origin (ADMIN_ORIGIN), through our own OIDC provider and see a placeholder page saying who they are and that they're an admin. A user who isn't an admin sees a 403 page showing their sub. Either can sign out, which also ends their session at our provider and leaves them on a signed-out page.
/admin/is registered only when sign-in andADMIN_ORIGINare configured; otherwise startup logs a warning. Every admin route answers only on the admin host, and nothing but/adminanswers there.- The admin pages are a built-in confidential client of our provider (not in
OIDC_CLIENTS), with its secret derived fromOIDC_COOKIE_KEYS. Sign-in is a server-side authorization code flow with PKCE, state and nonce; the token request reaches the provider in-process, so the API never calls its own public URL. - The session is an encrypted cookie holding
sub, sign-in time and ID token, lasting 8 hours,SameSite=Lax, scoped to/admin. A short-lived encrypted cookie holds each sign-in's state, verifier, nonce and the page to return to. - Every request checks the session's
subagainstADMIN_USERS. - POSTs check
Originagainst the admin origin; responses carry a CSP allowing only same-origin scripts and styles, and no framing. - Pages are Eta templates rendered with
@fastify/view, escaping by default, with a shared layout showing the signed-in user and sign-out.
See #26 for the full decisions.
Acceptance criteria
- With no session,
/admin/redirects to sign-in and returns to the page asked for afterwards - A user in
ADMIN_USERSsigns in through the fake upstream provider and sees the placeholder page with their name orsub - A user already signed in at our provider gets into admin without visiting the upstream provider
- A user not in
ADMIN_USERSgets a 403 page showing theirsuband a sign-out button - An empty or unset
ADMIN_USERSlets nobody in - Removing a user from
ADMIN_USERSstops their existing session - A session older than 8 hours is sent to sign in; a tampered or foreign cookie counts as no session
- A callback with a wrong state is refused
- Sign-out clears the session and redirects to the provider's end-session endpoint with
id_token_hint, ending on/admin/signed-out - A POST with a missing or foreign
Originis refused - Responses carry the CSP header; interpolated values are escaped
- Admin isn't registered when sign-in or
ADMIN_ORIGINisn't configured - ADR 0002 written, ADR 0001's consequence about which clients get tokens updated, Admin and Admin pages added to the glossary
-
ADMIN_USERSandADMIN_ORIGINdocumented in the README and.env.example - The admin pages answer only on the admin origin, and only
/adminanswers there
Blocked by
None (can start immediately)
Original request
Parent
What to build
An admin can sign in to the admin pages through our own OIDC provider and see a placeholder page saying who they are and that they're an admin. A user who isn't an admin sees a 403 page showing their sub. Either can sign out, which also ends their session at our provider.
/admin/is registered only when sign-in is configured; otherwise startup logs a warning.- The admin pages are a built-in confidential client of our provider (not in
OIDC_CLIENTS), with its secret derived fromOIDC_COOKIE_KEYS. Sign-in is a server-side authorization code flow with PKCE, state and nonce; the token request reaches the provider in-process, so the API never calls its own public URL. - The session is an encrypted cookie holding
sub, sign-in time and ID token, lasting 8 hours,SameSite=Lax, scoped to/admin. A short-lived encrypted cookie holds each sign-in's state, verifier, nonce and the page to return to. - Every request checks the session's
subagainstADMIN_USERS. - POSTs check
Origin; responses carry a CSP allowing only same-origin scripts and styles, and no framing. - Pages use an escaping
htmltagged-template helper and a shared, unstyled layout showing the signed-in user and sign-out.
See #26 for the full decisions.
Acceptance criteria
- With no session,
/admin/redirects to sign-in and returns to the page asked for afterwards - A user in
ADMIN_USERSsigns in through the fake upstream provider and sees the placeholder page with their name orsub - A user already signed in at our provider gets into admin without visiting the upstream provider
- A user not in
ADMIN_USERSgets a 403 page showing theirsuband a sign-out button - An empty or unset
ADMIN_USERSlets nobody in - Removing a user from
ADMIN_USERSstops their existing session - A session older than 8 hours is sent to sign in; a tampered or foreign cookie counts as no session
- A callback with a wrong state is refused
- Sign-out clears the session and redirects to the provider's end-session endpoint with
id_token_hint - A POST with a missing or foreign
Originis refused - Responses carry the CSP header; interpolated values are escaped
- Admin isn't registered when sign-in isn't configured
- ADR 0002 written, ADR 0001's consequence about which clients get tokens updated, Admin and Admin pages added to the glossary
-
ADMIN_USERSdocumented in the README and.env.example
Blocked by
None (can start immediately)
Metadata
Metadata
Assignees
Labels
ready-for-agentFully specified, ready for an AFK agentFully specified, ready for an AFK agent
Activity
johnf commentedon Oct 28, 2026
Updated to match PR #33; the previous text is kept under Original request. Changes: the admin pages are on their own origin, set by a new
ADMIN_ORIGINsetting (see #26 for why). Sign-out ends on/admin/signed-outinstead of signing straight back in. Pages are rendered as Eta templates via@fastify/view, not with the hand-rolledhtmlhelper.johnf commentedon Oct 28, 2026
Shipped in PR #33 (merge e2db8c2). All acceptance criteria met.