Skip to main content
After a user completes a second factor (TOTP today; a passkey assertion can use the same mechanism), the client can ask Pylon to remember this browser. Pylon then skips the second factor for 30 days on this device. The trust binds to the user. A stale cookie from a previous account on the same browser becomes untrusted; it never grants trust to a different user. The trust flag flows through auth.isTrustedDevice. Policies can use it to gate sensitive flows, for example to require a fresh TOTP code unless the device is trusted.

Endpoints

All three require a real session. API-key auth is refused with 403 API_KEY_AUTH_FORBIDDEN.

Minting trust

There is no dedicated endpoint to trust a device. Pylon mints trust as a side effect of a successful second-factor verify, when the user opts in:
When trust_device: true is set on /totp/verify, Pylon:
  1. Mints a 256-bit random trust token.
  2. Persists a TrustedDevice record bound to the user.
  3. Sets a pylon_trusted_device cookie (HttpOnly, SameSite=Lax, Secure in non-dev) with a 30-day lifetime.
The response includes trust_device: true to confirm:
The cookie is bound to the user through the underlying record. Stealing the cookie alone does not help an attacker: Pylon validates record.user_id == session.user_id on every request.

Listing devices

Response:
The response does not include the token. The cookie value stays on the server and never reaches the dashboard, so XSS that reads this endpoint cannot extract trust tokens. The label is parsed from the request’s User-Agent header at mint time. Browsers display it in the “active devices” account settings page.

Revoking

Revoke one device by id:
Pylon checks object-level authorization: it verifies that the record’s user_id matches the caller. A cross-user revoke attempt returns 404 NOT_FOUND, the same code as a missing device, so an admin cannot enumerate trusted-device ids from response timing. Revoke all devices at once:
Both responses include revoked: <count> for the number of records removed. Pylon also clears the current request’s trust cookie with Set-Cookie, so the browser drops it immediately.

Gating in your policies / handlers

The trust flag is available on AuthContext.is_trusted_device. Use it in TypeScript handlers:
In policies, the field is auth.isTrustedDevice (a boolean). Combine it with role checks to gate sensitive entity reads and writes.
  • Lifetime: 30 days from mint (DEFAULT_TRUST_LIFETIME_SECS = 30 * 24 * 60 * 60).
  • Cookie name: pylon_trusted_device.
  • Attributes: set from the framework’s CookieConfig, the same Secure, SameSite, and Path values as the session cookie. Operators do not configure two separate cookie policies.

Security guarantees

  • Token stays on the server. Listing trusted devices returns the id (a public handle) but never the cookie value. XSS that reaches /trusted-devices cannot extract trust tokens; it would need to read document.cookie, and HttpOnly blocks that.
  • Bound to the user. Every verify checks record.user_id == session.user_id. Stealing the cookie alone does not work; an attacker needs both the trust cookie and the matching session.
  • Object-level auth on revoke, with timing parity. A cross-user revoke attempt and a nonexistent device return the same 404 from the same code path.
  • Cleared automatically on full revoke. DELETE /trusted-devices (revoke all) clears the cookie on the response, so the browser drops it without a refresh.
  • HttpOnly, Secure, and SameSite=Lax, the same settings as the session cookie.

Where to go next

  • TOTP / 2FA: the verify endpoint that mints trust via trust_device: true.
  • Sessions: the base cookie attribute config that trusted-device cookies inherit.
  • Passkeys: a phishing-resistant alternative. A passkey user usually does not need a remember-device gate.