Skip to content

Let the owner sign out all browsers at once - #2080

Merged
Brian Krabach (bkrabach) merged 1 commit into
mainfrom
windows-ootb/sign-out-all
Nov 14, 2026
Merged

Brian Krabach (bkrabach) merged 1 commit into
mainfrom
windows-ootb/sign-out-all

Conversation

@bkrabach

Copy link
Copy Markdown
Collaborator

Change

Problem. A browser session is a signed cookie with no server-side list. It stays valid for the configured lifetime (seven days by default). The owner had no way to end a session early, for example after a phone is lost. This is the first of two steps that let a browser on another device sign in to a Windows host; two independent security reviews of that design asked for this control before remote sign-in ships.

Result. One new owner command, on every platform:

amplifier-unified browsers sign-out
  • It saves a time in the owner's private configuration (config/auth/session-epoch). It sends nothing over the network and uses no credential. The ability to write that private folder is the authority, so it also works while the host is stopped.
  • A running host notices the record within about two seconds (auth.watch_sessions; a cheap change hint, then a read through the private store). It then:
    • refuses every session from before that time, including one issued in the same second;
    • drops pending sign-in tickets, so a ticket from before the sign-out cannot produce a session after it;
    • ends open state streams, so a signed-out browser cannot keep watching. A stream that reconnects must pass the session check again.
  • One session check. The middleware and the two handlers that verified the cookie themselves (device_pairing, device_connections) now call auth.valid_session. A test fails if another module verifies a session cookie on its own.
  • The saved time never moves backward (a PC clock set back cannot revive a session). A failed save is an error with a plain message, never a silent success. A damaged or unreadable record signs everyone out once and is repaired.

No behavior change without the command. No record means no sign-out, and every valid session is accepted as before. Existing installations are not signed out by this update.

Owner. amplifier_web/auth.py (record, check, watcher), server.py (starts and stops the watcher), cli.py (command). docs/DEPLOYMENT.md has a new section, "Sign out all browsers".

Limits.

  • It signs out all browsers. There is no sign-out for one device.
  • Connected terminals, apps and devices with their own access are not browser sessions and stay connected. The stream end also reaches their open state streams once; they reconnect with their own credential.
  • A missing record means "no sign-out yet". The record is in the same owner-only folder as the session secret, so anyone who can delete it can also read that secret.
  • There is no button in the app and no Start-menu entry yet.

Validation

Local, Linux:

  • New tests/test_session_sign_out.py: 12 passed. It covers the check at the boundary second, the monotonic time, the private record, the damaged record, the failed save, the running-host application with ticket and stream effects, the watcher surviving a fault, the real middleware, and the real command.
  • New real-host test in tests/test_server_auth.py: a real create_app host with a signed-in cookie and an open /api/events stream. After the command's write, the host notices by itself; the same cookie gets 401 for the API and a redirect to sign-in for a page; the open stream ends; a later sign-in works.
  • Related suites: test_server_auth, test_browser_entry, test_device_connections, test_device_pairing, test_windows_user_trial_probe, test_session_sign_out: 976 passed, 3 skipped. Also test_companion_authority, test_history_scope, test_host_pam, test_acp_server, test_tls_deployment, test_runtime_import_isolation: pass.
  • The real command, run by hand against an empty data folder: exit 0, the record is owner-only (0600) and holds the current time.

Native Windows (this PR's windows-local-connection check): tests/test_session_sign_out.py runs there on the real private store (record written and read through the owner-only ACL boundary, damaged record, real command).

Not proven: a real browser on a real PC or phone being signed out. Linux systemd and macOS hosts are covered by the same code and tests, not by a live check.

A browser session is a signed cookie with no server-side list, valid for
the configured lifetime (seven days by default). There was no way to end a
session early, for example after a phone is lost. That is needed before a
Windows host can accept a browser on another device.

`amplifier-unified browsers sign-out` saves a time in the owner's private
configuration. It sends nothing over the network and uses no credential:
the ability to write that private folder is the authority, so it also works
while the host is stopped. A running host notices the record within about
two seconds. It then refuses every session from before that time, drops
pending sign-in tickets, and ends open state streams.

All browser session checks now go through one function,
`auth.valid_session`: the middleware and the two handlers that verified the
cookie themselves. The saved time never moves backward, a failed save is an
error, and a damaged record signs everyone out once and is repaired.
@bkrabach
Brian Krabach (bkrabach) merged commit fac91a6 into main Nov 14, 2026
17 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.

2 participants