fix(auth): end connected assistants, tokens and share links on sign-out everywhere

"Sign out everywhere" ended web sessions and device logins but left every
credential that works without signing in: a connected AI assistant kept its
OAuth connection and minted the next access token from it, a connector or
ingest token kept working, and a clinician share link kept opening the
record. A password change revoked the tokens but not the assistant's
connection or the share links. Anything a stolen session had minted
outlived the remedy.

- Sign out everywhere now ends every other sign-in, every AI-assistant
  connection, every programmatic token except the caller's own, and every
  clinician share link. Share links are revoked by default; the web offers
  a checkbox to keep them (keepShareLinks=1), and a client that sends
  nothing, such as the shipped app, gets the safe default.
- A password change and an admin password reset also end AI-assistant
  connections, share links and step-up elevations.
- Turning on the first second factor (TOTP or a security key) ends every
  other sign-in, as a password change does. Adding a further factor does
  not.

destroyOtherSessions takes an explicit reach ("sign-ins" or "everything")
so each caller says how far it goes.
This commit is contained in:
Marc-André Bombeck committed 2026-09-26 20:06:36 +02:00
1 parent 0fc83cb0a1
commit 92c5b0a60d
23 files changed
+658 -66

No files matched your search

+34 -2
View File
@@ -2590,8 +2590,21 @@ paths:
tags:
- Auth
summary: Sign out everywhere else
description: v1.23 — revokes every OTHER web session plus all native refresh tokens, keeping the caller's current
session. API tokens are not touched (manage those under /settings/api-tokens).
description: "Ends every credential except the caller's own: every OTHER web session, every other device login (refresh
token and its paired access token), every trusted device and step-up elevation, every connected AI assistant
(the OAuth connection, so it cannot mint a new access token), every programmatic token (connector, measurement
and document tokens), and every clinician share link. Share links are revoked by default because a link made by
whoever held a lost session or device would otherwise keep opening the record; send `keepShareLinks=1` to keep
them. A Bearer caller keeps its own device login."
parameters:
- in: query
name: keepShareLinks
schema:
description: "`1` keeps the clinician share links. Anything else, or nothing, revokes them."
type: string
enum:
- "1"
description: "`1` keeps the clinician share links. Anything else, or nothing, revokes them."
responses:
"200":
description: Other sessions revoked.
@@ -38525,8 +38538,27 @@ components:
minimum: -9007199254740991
maximum: 9007199254740991
description: Number of OTHER sessions removed (the current one is kept).
accessTokensRevoked:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
description: "Tokens revoked: the access tokens of other device logins plus every programmatic token (connector,
measurement, document), never the caller's own."
connectorsRevoked:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
description: Connected AI-assistant (MCP OAuth) connections revoked.
shareLinksRevoked:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
description: Clinician share links revoked; 0 when `keepShareLinks=1` was sent.
required:
- sessionsRevoked
- accessTokensRevoked
- connectorsRevoked
- shareLinksRevoked
additionalProperties: false
SessionRevokeEnvelope:
type: object