Skip to content

fix: bind admin sessions to the signing-in device (beta, off by default) - #688

Merged
andris9 merged 1 commit into
masterfrom
dbsc-admin-sessions
Nov 5, 2026
Merged

andris9 merged 1 commit into
masterfrom
dbsc-admin-sessions

Conversation

@andris9

@andris9 andris9 commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Adds opt-in Device Bound Session Credentials (DBSC) for the admin UI, as a quiet beta like MCP: off by default, a Beta badge on the Security page, no README mention.

What it does. With dbscEnabled on (Configuration > Security, privileged setting), Chrome on Windows and macOS binds each admin sign-in to a key in the device's TPM or Secure Enclave. A bound session's requests, including the message browser's sess_ API calls, need a 10-minute iron-sealed eeb cookie that only the key can renew, so a copied session cookie stops working within minutes. Browsers without support never register and work as before. Chrome only binds on an https origin with a trusted certificate, so the row reports itself inactive without an https Service URL.

Design notes (also in CLAUDE.md):

  • Nothing deletes a binding. Logout marks it ended, so a copy of the cookie stays refused after the owner signs out. The first version deleted the binding at logout; /security-review caught that.
  • A session binds once (SET NX), and challenges are single use and tied to their session and purpose.
  • Refresh answers in terms the browser acts on: {"continue": false} for an ended, unknown or switched-off session, a 403 challenge, and a 401 after repeated bad proofs.

Verification.

  • Protocol checked against Chrome 156 on macOS before any code was written, through a throwaway server and a cloudflared tunnel.
  • test/dbsc-test.js drives the real routes and extensions on a hapi server with generated ES256/RS256 keys.
  • test/dbsc-jwt-test.js includes two proofs Chrome actually sent.
  • test/e2e/pages-dbsc.spec.js runs a signed Chrome against the e2e instance through a public https URL. It passes locally and skips in CI, which lacks a platform key store.
  • Unit 5681/5681, integration 264/264.
  • E2E 137/141; the 4 failures are the pre-existing pages-admin overlay flakes and a token-page flake that also fails on clean master.
  • Security page reviewed with screenshots in every state (off, on, inactive, bound), light and dark, desktop and 390px.

A signed-in admin session is a cookie, and malware that copies the browser's
cookie store can replay it from another machine. The new dbscEnabled setting
(Configuration > Security, Beta) binds each admin sign-in to a key the browser
keeps in the device's security hardware, using Device Bound Session
Credentials. A bound session's requests, the message browser's API calls
included, need a second, iron-sealed cookie that lives ten minutes and that
only the key can renew, so a copied session cookie stops working within
minutes. Logout marks the binding ended rather than deleting it, so a copy of
the cookie stays refused after the owner signs out.

Browsers without support (anything but Chrome on Windows and macOS today)
never register and sign in as before. Chrome only binds on an https origin with
a trusted certificate, so the setting reports itself inactive without an https
Service URL.

lib/dbsc/ holds the protocol (proof verification on Node's own crypto, single
use challenges and bindings in Redis), lib/ui-routes/dbsc-routes.js the
registration and refresh endpoints. Verified end to end against Chrome 156 on
macOS: test/dbsc-test.js covers the protocol on a real hapi server,
test/dbsc-jwt-test.js includes proofs Chrome actually sent, and
test/e2e/pages-dbsc.spec.js drives a signed Chrome through a public https URL
when one is configured.
@andris9
andris9 merged commit a62a421 into master Nov 5, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant