config/authentication.php controls every aspect of token signing and verification. All guards using the jwt driver read these defaults; individual guards can override any field through a jwt sub-block in config/auth.php. The service fails closed — if signing material is absent or invalid when a JWT guard or token service is resolved, it throws rather than silently accepting forged tokens.
Full config block
This is the publishedjwt section of config/authentication.php:
config/authentication.php
Fields
The HMAC signing secret for single-secret mode. Sourced from the When
AUTHENTICATION_JWT_SECRET environment variable. Must be at least 32 bytes of random data; shorter values are technically accepted but weaken the signature. The keyring fails closed — an empty or missing secret throws InvalidJwtConfigurationException at boot rather than allowing unsigned tokens through.Set this in your environment:.env
keys is non-empty, secret is ignored. You do not need both.A Kids and secrets must be non-empty strings. An empty kid string or empty secret value throws at boot.
kid → secret map that enables kid-based key rotation. When this map is non-empty, it takes precedence over secret. Each entry maps a key identifier string to its HMAC secret. All kids present in the map are accepted on verification; only the active_kid is used for signing new tokens.config/authentication.php
The kid from the
keys map that signs newly issued tokens. The kid is embedded in the JWT header so the verifier can select the correct key. This field is required when keys is non-empty — if active_kid is empty or points to a kid that does not exist in keys, the keyring throws at construction..env
The JWS signing algorithm. The package allow-lists the following algorithms; any other value throws at boot:
HS256,HS384,HS512RS256,RS384,RS512ES256,ES384
.env
Lifetime of an access token in minutes, written into the
exp claim. After this window, plus any leeway_seconds, the token is rejected. The default of 15 minutes is deliberately short; access tokens are self-verifying and cannot be revoked server-side without a blocklist..env
Lifetime of a refresh token in minutes, written into the
exp claim. The default is 43200 (30 days). Refresh tokens are stateful — they are backed by the devices table and subject to replay detection regardless of this TTL..env
Clock-skew tolerance applied to every
exp and iat check during token verification. A token whose exp falls within leeway_seconds of the current server time is still accepted. The default of 30 seconds accommodates reasonable clock drift between the issuer and verifier. Do not set this to a large value — it extends every token’s effective lifetime..env
Optional. When set, the
iss claim is embedded in every issued token and strictly verified on every parse. Tokens whose iss claim does not exactly match this value are rejected. Omit to skip issuer verification entirely..env
Optional. When set, the
aud claim is embedded in every issued token and strictly verified on every parse. Tokens whose aud claim does not exactly match are rejected. This is the primary mechanism for isolating multiple JWT guards — each guard can carry its own audience so tokens minted by one guard cannot authenticate against another..env
Key rotation
For production deployments that need graceful signing-key rotation, configurekeys and active_kid instead of secret. The verifier accepts every kid present in the map; only active_kid signs new tokens. To rotate:
Add the new kid
Add the new kid and its secret to the
keys map. Deploy with active_kid still pointing at the old kid — both kids now verify, but new tokens continue to use the old signing key.Promote the new kid
Update
active_kid to the new kid. New tokens are now signed under the new key; old tokens signed under the previous kid continue to verify until they expire.config/authentication.php
Per-guard JWT overrides
Everyjwt guard inherits its signing material, audience, issuer, TTLs, and leeway from the package-wide authentication.jwt.* defaults. You can override any of these per guard by adding a jwt sub-block to the guard’s entry in config/auth.php. Fields you omit fall back to the package defaults.
This lets you run multiple JWT guards with distinct trust boundaries from a single app — tokens minted by one guard carry that guard’s aud claim and cannot authenticate against another:
config/auth.php
auth:staff or auth:customer middleware. Issue tokens through the guard context so the correct signing material and audience claim are applied:
jwt block is overridable per guard: secret, keys, active_kid, algorithm, access_ttl_minutes, refresh_ttl_minutes, leeway_seconds, issuer, and audience. Guards can carry fully independent kid-rotation maps if you need separate signing-key lifecycles per trust boundary.