Access tokens are self-verifying JWTs — there is no server-side access-token store and no revocation list the guard consults. On the bearer path, the guard verifies the signature and claims, then rehydrates the identity and principal from the database. Revoking a device blocks future refresh exchanges, but access tokens already in flight remain valid until their
exp claim is reached.Get the JWT token service
Access the guard-scoped token service through theAuth facade. Pass the guard name, or omit it to use the default guard:
JwtTokenService is already configured with the signing material, algorithm, TTLs, issuer, and audience for that specific guard.
Issue an access token
CallissueAccessToken() with the authenticated identity, the acting principal, and the device. In 2D mode the identity and principal are the same model instance. Pass null for the device if you are running in access-only mode without device tracking.
| Parameter | Claim embedded | Notes |
|---|---|---|
$identity | sub | The identity’s auth identifier (e.g. UUID or integer primary key) |
$principal | pid | The principal’s stable identifier from getPrincipalIdentifier() |
$device | did | The device’s identifier, or null in access-only mode |
jti (random token id), iat (issued-at), exp (expiry), typ: "access", and optionally iss and aud if your guard configuration includes them.
Issue a refresh token
CallissueRefreshToken() on the guard or its token service after issuing the access token. You must generate a plaintext rotation id, embed it in the refresh JWT, and store its hash on the device row — never the plaintext value.
$principal parameter is optional. When provided, the refresh token carries a pid hint that the guard validates during rotation — the resolved principal must match the hint or the exchange fails closed.
Refresh token claims: did (device identifier), jti (the plaintext rotation id), iat, exp, typ: "refresh", and optionally pid, iss, aud.
Typical login response
A standard login controller issues both tokens and returns them to the client in a single response:Access-only mode
If your app does not need device tracking or refresh rotation — common for M2M APIs and short-lived session flows — skip the devices migration and passnull as the device:
did: null. On the bearer path, Auth::device() returns null. The full identity/principal context (Auth::identity(), Auth::principal(), Auth::tenant(), Auth::type()) still works normally. You can add device tracking and refresh later — it is purely additive.