How refresh works
Callrefresh() on the guard instance — not on the Auth facade’s JWT service — and pass the raw refresh token string from the client. The method returns a RefreshResult on success, or null on any failure.
RefreshResult contains two public string properties:
On failure,
refresh() returns null. A RefreshFailed event has already been dispatched by the time null is returned — you do not need to dispatch it yourself.
The full exchange pattern
Refreshed event so your listeners can distinguish a rotation from an initial authentication.
Failure handling and the RefreshFailed event
Whenrefresh() returns null, a RefreshFailed event has been dispatched carrying a RefreshFailureReason backed enum. Listen to this event in your SIEM pipeline or for rate-limiting logic:
Security properties
Atomic per-device rotation. The rotation digest update and the new token pair are produced in a single operation scoped to the device row. Two concurrent exchange attempts on the same device cannot both succeed. Replay detection. If the package detects that a rotation id has already been consumed — which happens when a client presents a token that was valid in a previous rotation cycle — it treats this as evidence of a compromised or leaked token. The device is immediately revoked and aRefreshFailed event is dispatched with reason rotation_reuse. Any future refresh attempts for that device will fail with device_revoked.
Constant-time digest verification. The stored rotation digest is compared against the presented token using hash_equals() to prevent timing side-channel attacks.
pid hint validation. When the refresh token carries a pid claim, the guard resolves the principal and confirms it matches the hint before completing the exchange. A mismatch produces principal_mismatch and the exchange fails closed.
Revoking devices
Revoking a device row prevents all future refresh exchanges for that device. Access tokens already issued to that device remain valid until theirexp claim is reached — the guard does not consult access-token jti values on the bearer path. For immediate access revocation, shorten your access-token TTL or implement CanBeActive on your identity model so the guard rejects inactive identities on every bearer request.