rcerljenko/laravel-jwt
A lightweight JWT authentication package for Laravel. It provides a guard, middleware, and token helpers for issuing, validating, and refreshing JSON Web Tokens, making it easy to secure APIs with stateless authentication. Simple setup and Laravel-friendly configuration.
rcerljenko/laravel-jwt is a thin JWT auth layer for Laravel APIs — a guard, a middleware, and a trait you put on your User model. It is not a full auth scaffold, so budget an afternoon to wire it into an existing app.
Minimal path:
composer require rcerljenko/laravel-jwt (2.1.0 is additive over 2.0.x — no breaking changes, safe bump).php artisan vendor:publish --tag=laravel-jwt-config and check config/jwt.php first — this file is where token TTL, the signing algorithm, and (new in 2.1.0) the cookie transport settings live.JWT_SECRET in .env. Generate one rather than typing it; rotating this later invalidates every issued token.config/auth.php under guards, pointing driver at the package's guard and provider at your users provider.Your first use case should be a single POST /api/login that validates credentials and returns a token — everything else (refresh, logout, revocation) is a variation on that. If you are building a browser SPA rather than a mobile/CLI client, jump straight to the 2.1.0 cookie mode described below, because retrofitting it later means touching every client.
Stateless bearer flow (the default). Login controller returns ['token' => $token]; clients send Authorization: Bearer <token>. Keep token issuance in one place — a small JwtService or an action class — so TTL and claims stay consistent across login, refresh, and impersonation paths.
Cookie transport (new in 2.1.0). The package can now read the token from a cookie instead of the Authorization header. This is the right call for server-rendered apps and first-party SPAs, since an HttpOnly cookie is not reachable from JavaScript. Pattern:
HttpOnly, Secure, SameSite=Lax or Strict).supports_credentials => true and an explicit origin (never *), and that the frontend sends withCredentials: true (Axios) or credentials: 'include' (fetch).localhost:3000 doesn't shadow a staging token.Dual transport. Because the header takes priority and the cookie is a fallback (verify this against the guard's resolution order in your version), you can serve a mobile app and a web app from the same endpoints. Be deliberate: if you accept both, document which one wins, and consider rejecting ambiguous requests in a small middleware of your own.
Revocation and logout. Short TTL plus a refresh endpoint is cheaper than a blacklist. If you need immediate logout, use the package's blacklist/invalidate support and confirm your cache driver is not array in production — an ephemeral store silently makes revocation a no-op.
Testing. In feature tests, $this->actingAs($user, 'api') usually bypasses the token entirely; write at least one test that goes through the real login endpoint and one that asserts a request with a tampered or expired token gets a 401. For cookie mode, assert on Set-Cookie via $response->assertCookie() and drive follow-up requests with withCookie().
JWT_SECRET rotation logs everyone out. That is correct behaviour, but plan it — deploy the new secret, keep the old one accepted during a grace window if the package allows multiple keys, or accept a hard logout.config/auth.php bites when you add JWT alongside session auth. Laravel uses the first guard that returns a user; if web is first and a session cookie exists, your API route may authenticate via session and never touch the JWT.Set-Cookie actually in the response? (2) does the browser store it — check Domain, Secure on http://localhost, and SameSite? (3) is the browser sending it — check CORS credentials and withCredentials? Roughly 90% of "cookie auth doesn't work" reports die at step 2.localStorage and also in a cookie. Pick one. Duplicated transport is how you get a token that survives logout in one place and not the other.^2.1) in composer.json and skim the diff on each bump; the changelog is terse, as 2.1.0's one-line note shows.How can I help you explore Laravel packages today?