Skip to main content

GET /subjects/:id/sessions and DELETE…

GET /subjects/:id/sessions and DELETE /subjects/:id/sessions/:sid​

Both require manage-sessions; there is no view-sessions, the same reasoning that leaves clients with no view-clients — a session is reached only by an operator who can also end one.

GET lists the subject's live sessions — the same liveness arithmetic every other session consumer applies, each session measured against the idle window its own remembered column picks. This is the one read keyed on the subject rather than on a browser's cookie, so it is also the one place an operator can see a session the cookie no longer names — a second concurrent login can orphan one, and a remembered orphan idles for remember_me_idle_seconds before it stops appearing (ADR 0033's amendment). Each entry carries id, created_at, last_active_at, remembered, and client_ids — the OAuth client_id of every enabled client the session holds a grant for. Paginated the same way every other list in this API is: ?limit= and ?cursor=, a Link: rel="next" header and a body next while more remain, ordered by id — a subject's sessions are bounded per browser by max_sessions_per_browser, but unbounded across however many browsers hold one, so this listing pages exactly like the others rather than trusting that bound.

DELETE ends one session through the same call the RP-Initiated Logout usecase makes (endSession) — there is one path that ends a session, not two. It revokes every grant the session holds and enqueues a Back-Channel Logout Token for each registered client that used it and has a backchannel_logout_uri configured (§2.5 of the spec). Front-Channel Logout does not apply here: §3 renders an iframe per relying party in the End-User's own browser, and an admin-initiated end has no browser to render one in, so only the back-channel delivery is attempted. Beside its own admin_mutation row, session.end, it writes the session.ended row every session end writes, with detail.via admin (request paths). A second DELETE of the same session is idempotent and answers 204: ending an already-ended session moves neither stamp, and a repeat delivery for the same client is deduped by backchannel_logout_deliveries_dedupe. expires_at moves only while it is still ahead of now, and coalesce(revoked_at, now) keeps the first revocation — a bare assignment would push both stamps forward on every repeat, so a second DELETE at a later moment would delay the reaping the first one started rather than changing nothing. The repeat writes its own admin_mutation row, since it is a request an administrator made, but no second session.ended. An unknown session id, or one belonging to a different subject, answers 404.

A session needs a login, and a subject this API created has no password, so this section runs against bob — seeded with seed user --tenant demo --username bob --password …, subject 01a0d6fd-ede7-704b-8d83-fa3801d427a0 — signing in through demo-app, a public authorization_code client created for it. verify_email was set back to false first: PATCH /settings above had turned it on, and with it on the login ends on "Can't sign in yet" rather than in a session.

curl -sS \
-H "Authorization: Bearer $ADMIN_TOKEN" \
http://localhost:3000/admin/tenants/demo/subjects/01a0d6fd-ede7-704b-8d83-fa3801d427a0/sessions
{"items":[{"id":"01a0d6fe-9d8f-783b-b244-ea4a85ab9bfb","created_at":"2026-09-25T05:16:45.837Z","last_active_at":"2026-09-25T05:16:45.837Z","remembered":false,"client_ids":["demo-app"]}]}

DELETE twice over the same id, then the listing again — 204 both times, and nothing left:

curl -sS -D - -X DELETE \
-H "Authorization: Bearer $ADMIN_TOKEN" \
http://localhost:3000/admin/tenants/demo/subjects/01a0d6fd-ede7-704b-8d83-fa3801d427a0/sessions/01a0d6fe-9d8f-783b-b244-ea4a85ab9bfb
HTTP/1.1 204 No Content
x-request-id: 01a0d6fe-9df1-760b-8e13-7763f4c28a3d

HTTP/1.1 204 No Content
x-request-id: 01a0d6fe-9e09-7004-9844-b3c1b3abb219

{"items":[]}

(This read predates the cache-control: no-store header and was not re-run.)