Sessions and devices

A session is not a device

A session is one live credential — the refresh half of the pair, specifically. A device is the thing holding it, and one device can hold several sessions over its life, because every rotation mints a new one.

The registry exists so a user can act on the second thing. Sign out my old phone is a sentence about a device, and answering it needs a durable id that survives every rotation in between.

A client opts in with headers

Send x-pithy-device-id at sign-in and the device is registered. A mobile client is where this earns its place. Send nothing and it is not — a device-less sign-in is a perfectly ordinary web sign-inb session, and nothing about the flow changes.

HeaderRequired
x-pithy-device-idYes. No id, no registration
x-pithy-platformNo
x-pithy-device-nameNo
x-pithy-device-modelNo
x-pithy-os-versionNo
x-pithy-app-versionNo
x-pithy-push-tokenNo

The id is yours to generate and yours to keep stable. A value that changes per launch turns the registry into a list nobody can read.

A sparse re-login never erases what you already knew

The upsert is idempotent on (userId, deviceId). A returning device refreshes lastSeenAt and lastIp — always — and refreshes any other field only when this request actually supplied it.

That distinction is the whole design of the write. A client that sent full metadata on first sign-in and only an id on the next one would otherwise have its model, OS version and name overwritten with blanks, and the row that read iPhone 15 Pro · iOS 18.2 would become an id and nothing else. An absent header means no news, not clear it.

platform shows this most clearly: it defaults to web only on first registration. On an update, an absent platform header leaves the stored value alone, so a sparse re-login from a phone never relabels it as a browser.

createdAt is never reset. It is when this device first appeared, and that is a fact about the past.

Two routes a user’s own client calls

Both are in the reference with their shapes.

RouteWhat it does
GET /auth/devicesThe caller’s devices, most recently seen first
POST /auth/devices/revokeSign out one device, then forget it

GET needs a valid credential and nothing more. The revoke also carries the CSRF origin guard, because it is a mutating route and a cookie session is a credential a browser attaches for you.

Revoking is two acts in one order. Every session token bound to that device is deleted through Better Auth’s own session deletion — so its bookkeeping stays consistent — and only then is the device row removed. The response is a count:

{ "revoked": 2 }

Two, not one, because a device that rotated since its last cleanup can legitimately hold more than one live row.

An audit event is written. Somebody signed out a device is exactly the kind of event that matters later, when the question is when a phone stopped having access.

The admin side is a separate surface

An operator revoking somebody else’s sessions is a different act from a user revoking their own, and the kit treats it as one:

RouteVerified by
GET /auth/admin/userscontrol-plane
GET /auth/admin/users/:userIdcontrol-plane
GET /auth/admin/devicescontrol-plane
POST /auth/admin/sessions/revokecontrol-plane
POST /auth/admin/users/:userId/sessions/revokecontrol-plane
POST /auth/admin/users/:userId/devices/revokecontrol-plane

These need an issued, scoped machine credential — never a staff member’s session. A support agent who can sign a customer out because their own cookie happens to carry a role is an audit trail that cannot distinguish the tool did it from a person did it, and default-denied is the state these routes ship in.

Where a device binding shows up again

The binding is carried across rotation: a successor session inherits the device id from the one it replaces. So the registry stays accurate over a session’s whole life without the client re-sending anything, and a revoke months later still finds every row that device is holding.

A refresh-token family and a device are different groupings, and both can revoke. A family revocation is the automatic response to a detected replay. A device revocation is a person deciding. Neither is a substitute for the other.

ESC