feat(auth): per-user device list + per-device session revoke #169
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up from PR #168 (session-expiration-policy).
Motivation
The v1 surface from #168 supports account-wide bulk revocation (
POST /accounts/me/security/revoke-sessionswithscope: all|others). The follow-up is to let any user see their own active devices and revoke individual sessions — the classic Slack/Google "Where youre signed in" surface. Also unlocks finer-grained breach response.Scope
refresh_tokens—user_agent VARCHAR(500),ip VARCHAR(45),last_used_at TIMESTAMPTZ. Stampuser_agent+ipat_mint_session_tokens/_mint_with_claimsfrom the FastAPIRequest. Updatelast_used_aton every/auth/refresh(in the same atomic-revoke statement is cleanest —RETURNINGthe row andINSERTing the new one withlast_used_at = now()).GET /me/sessionsreturns the current users active refresh-token rows with{jti_prefix, user_agent, ip, created_at, last_used_at, is_current}.is_currentset by comparing JTI hashes.DELETE /me/sessions/{jti_prefix}revokes one specific token (audited)./account/profile) or new/account/devicespage — list of "Chrome on macOS · Last active 2h ago · IP 1.2.3.4 · Sign out" rows.user_agentto friendly device label (react-device-detector similar). Geo lookup onipfor "Signed in from San Francisco" copy.Out of scope
Why deferred from #168
The original plan called it out (
docs/plans/2026-05-13-session-expiration-policy.md§4.11 and §9 follow-up #2). Account-wide bulk revoke covers the breach-response use case for v1 — "I lost my laptop" still means "sign me out everywhere." Per-device is the next polish layer. Adds a column migration + tracking work that the v1 ship didnt need.Dependencies
None. Lands cleanly on top of PR #168.
Effort
~1-2 days. Migration + 2 endpoints + 1 page + audit-event type.