a user can mint a long-lived key for an app or a device instead of
carrying a 30-day session, so multiple logins on the mobile apps are
per-device revocable rather than one shared token.
identity was being decided independently in userMiddleware and
originScopeMiddleware, each verifying the token itself. teaching only
one of them a new credential format is how those two stop agreeing, so
both now call resolveAuthToken and neither knows what a bearer string
is. verified: a member's key returns the same status as their jwt on
every route tried, 403s included.
a key carries its holder's full authority — not an escalation, it
equals what the password could already do. scoping wants a scopes
column, not a change here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- token-store: shared "give me a valid Vaultwarden access token" (proactive
refresh) used by both the HTTP proxy and the WS; router refactored onto it.
- notifications WS: validates the platform session in `open` (deferred, owner
only), injects the stored Vaultwarden token into the upstream, and buffers
client frames during the async setup so the SignalR handshake isn't dropped.
The device connects with its platform JWT (?access_token=), never a vault one.
- lifecycle: logout drops the vault token set (keeps the protector); distress
(/auth/revoke) and panic wipe both token set and protector, forcing a
one-time master-password re-setup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>