feat(auth): add device session tracking with configurable limits
Track authenticated sessions per device (parsing user-agent for desktop vs mobile via mileusna/useragent), enforce configurable concurrent device limits (Auth.DeviceLimits), and surface device status and "remove other devices" management in the account dialog. Adds device/session context to the auth module stores and exposes limits through the API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+38
-3
@@ -190,15 +190,50 @@ Updates account display name and avatar URL. The frontend uploads avatar files t
|
||||
|
||||
`GET /api/account/devices`
|
||||
|
||||
Returns the current local web session. This version does not maintain a multi-device session registry yet, so the current device is read-only and cannot be removed.
|
||||
Returns every non-revoked, non-expired web session for the authenticated account. The current session is listed first, followed by most recently active sessions.
|
||||
|
||||
```json
|
||||
{
|
||||
"devices": [
|
||||
{
|
||||
"id": "62dc9af7-e44a-4f47-a0bc-b3408d17c33f",
|
||||
"online": true,
|
||||
"current": true,
|
||||
"deviceType": "desktop",
|
||||
"system": "macOS",
|
||||
"browser": "Chrome 126",
|
||||
"client": "PC-WEB",
|
||||
"lastSeenAt": "2026-07-11T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"limits": {
|
||||
"desktop": 2,
|
||||
"mobile": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`limits` is sourced from `Auth.DeviceLimits.Desktop` and `Auth.DeviceLimits.Mobile` in service configuration. It is returned with the device list so clients display the effective policy without hard-coded counts. Defaults are 2 desktop web sessions and 1 mobile web session.
|
||||
|
||||
Each signed access and refresh token carries an opaque random session ID. Bearer authentication validates that server-side session before accepting the token, so revoked sessions stop working immediately. Device metadata is derived from request headers for display only and is never an authorization signal. A session is reported online when it is current or was active during the previous five minutes.
|
||||
|
||||
`lastSeenAt` is an RFC3339 UTC instant. Clients must render it in the browser's local timezone rather than displaying the UTC wire representation directly.
|
||||
|
||||
Valid legacy access tokens without a session ID are migrated to a revocable server-side session on first use after their user subject is verified. This preserves existing logins during rollout.
|
||||
|
||||
`DELETE /api/account/devices`
|
||||
|
||||
Removes non-current devices when a future server-side session registry exists. In the current local-only implementation, it returns `{"removed": 0}`.
|
||||
Atomically revokes every active session for the account except the current session. Returns the number of sessions revoked:
|
||||
|
||||
```json
|
||||
{
|
||||
"removed": 2
|
||||
}
|
||||
```
|
||||
|
||||
`POST /api/account/logout`
|
||||
|
||||
Records a logout request server-side and returns `{"removed": 0}`. The frontend then clears its locally stored session.
|
||||
Revokes the current server-side session and returns `{"removed": 1}` when it was active. The frontend then clears its locally stored token. Other device sessions remain signed in.
|
||||
|
||||
## Projects
|
||||
|
||||
|
||||
Reference in New Issue
Block a user