Reader Stacks

How to Implement JWT Authentication in Laravel

Setting up tymon/jwt-auth for a stateless API, and why JWT is the wrong choice for a normal server-rendered app with sessions.

How to Implement JWT Authentication in Laravel

JWT (JSON Web Token) auth is for stateless clients — a mobile app or a separate SPA frontend calling your Laravel API — where there's no shared session cookie between client and server. If you're building a normal server-rendered Laravel app, Laravel's built-in session-based auth is simpler and more secure; JWT solves a problem that setup doesn't have.

1. Install the package

composer require tymon/jwt-auth
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

jwt:secret generates and sets JWT_SECRET in your .env — this is the signing key for every token; treat it with the same care as APP_KEY.

2. Configure the User model

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims(): array
    {
        return [];
    }
}

3. Set the guard

In config/auth.php, point the api guard's driver to jwt:

'guards' => [
    'api' => ['driver' => 'jwt', 'provider' => 'users'],
],

4. Issue a token on login

public function login(Request $request)
{
    $credentials = $request->only('email', 'password');

    if (! $token = auth('api')->attempt($credentials)) {
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    return response()->json(['access_token' => $token, 'token_type' => 'bearer']);
}

5. Protect routes

Route::middleware('auth:api')->group(function () {
    Route::get('/me', fn (Request $request) => $request->user());
});

Token expiry and refresh

Tokens expire (default 60 minutes, set via JWT_TTL). Build a refresh endpoint rather than issuing very long-lived tokens — a stolen long-lived token is valid until it naturally expires, with no server-side way to revoke it early unless you also maintain a blocklist, which defeats much of the point of a stateless token.

JWT does not remove the need for HTTPS or secure token storage

A signed token protects its integrity; it does not encrypt the claims inside it. Anyone who obtains the token can normally decode its payload, and anyone who steals a valid bearer token can use it until it expires or is otherwise invalidated. Never place secrets in JWT claims, always transmit tokens over HTTPS, and treat client-side token storage as part of the threat model rather than an implementation afterthought.

Keep the payload small and authorization current

It is tempting to put roles, permissions, profile data, and other application state into every token so the API can avoid a database read. The tradeoff is staleness: if a user's permission changes, an already-issued token still contains the old claim until it expires. Put only claims that genuinely belong in the token, and re-check server-side state for authorization decisions that must take effect immediately.

JWT and Laravel Sanctum solve overlapping but different problems

Laravel's first-party Sanctum is often simpler for first-party SPAs, mobile apps, and personal API tokens because it integrates directly with Laravel's authentication model and can use cookie-based SPA authentication. A third-party JWT package is appropriate when the system specifically needs JWT interoperability or stateless bearer tokens across services. Do not choose JWT only because the client is JavaScript; the client type alone does not require it.

Refresh flow is part of the security design

A refresh endpoint should authenticate the refresh operation, rotate or invalidate tokens according to the package's supported model, and return predictable errors when a token is expired or invalid. Test expiry and refresh behavior explicitly. Login working once is not enough — most production JWT failures appear later, when clocks, expiry, logout, refresh, or revoked access have to behave consistently.

Topics: Authentication & Access Control APIs & Integrations