Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Jwt Laravel Package

codecasts/laravel-jwt

JWT authentication for Laravel: generate and validate JSON Web Tokens, protect routes with auth middleware, and add stateless token-based authentication to APIs and apps. A lightweight integration for securing Laravel endpoints with configurable JWT guards.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install (Laravel 5.x era package):
    composer require codecasts/laravel-jwt
    
  2. Register the service provider and facade in config/app.php if your Laravel version doesn't auto-discover it:
    // providers
    Codecasts\JWT\Providers\LaravelServiceProvider::class,
    // aliases
    'JWTAuth' => Codecasts\JWT\Facades\JWTAuth::class,
    
  3. Publish and configure the config, then set the shared secret:
    php artisan vendor:publish --provider="Codecasts\JWT\Providers\LaravelServiceProvider"
    # .env
    JWT_SECRET=base64:...   # long, random, same value across app servers
    
  4. Add the guard in config/auth.php:
    'guards' => [
        'api' => ['driver' => 'jwt', 'provider' => 'users'],
    ],
    
  5. Register middleware in app/Http/Kernel.php (jwt.auth, jwt.refresh are the names to look for first).
  6. First use case — issue a token on login:
    Route::post('auth/login', function (Request $request) {
        $token = JWTAuth::attempt($request->only('email', 'password'));
        if (! $token) {
            return response()->json(['error' => 'invalid_credentials'], 401);
        }
        return response()->json(compact('token'));
    });
    
  7. Protect a route and confirm the whole loop works before building anything else:
    Route::get('me', fn () => auth('api')->user())->middleware('jwt.auth');
    
    If auth('api')->user() returns the model from the Bearer header, the integration is sound — everything else is layering.

Implementation Patterns

Dedicated auth controller, not closures. Keep token issuance in a small controller (LoginController, RefreshController, LogoutController) so you can unit-test issuance and swap the transport later.

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $token = JWTAuth::attempt($request->only('email', 'password'));
        return $token
            ? response()->json(['token' => $token, 'token_type' => 'bearer'])
            : response()->json(['error' => 'invalid_credentials'], 401);
    }
}

Read the user through the guard, not through the token. The idiomatic path is auth('api')->user() inside controllers and policies; it resolves the token from the request, validates it, and loads the User provider. Use JWTAuth::parseToken()->authenticate() only when you need the token object itself (e.g., to invalidate).

Stateless login/logout endpoints. Login returns a token, logout invalidates the presented token:

public function logout()
{
    JWTAuth::invalidate(JWTAuth::getToken());
    return response()->noContent();
}

Because the guard is stateless there is no session to clear — logout is purely a token operation.

Refresh with explicit endpoints, not jwt.refresh middleware everywhere. The jwt.refresh middleware swaps the old token for a new one on each request and (typically) returns it in a response header. That's convenient for internal SPAs but produces a token stream that's hard to debug and painful for mobile clients. Prefer a POST /auth/refresh that returns {token: ...} and let clients call it near expiry.

Protect groups of routes with the middleware, then lean on policies. Don't sprinkle token checks in controllers:

Route::middleware('jwt.auth')->group(function () {
    Route::apiResource('posts', PostController::class);
    Route::get('me', fn () => auth('api')->user());
});

Authorize against $request->user() in policies exactly as you would with session auth.

Multi-tenancy / multiple identities. Add a second jwt guard with a different provider (e.g., admins) when you need separate token audiences; the guard driver is generic over the provider, so this is cheaper than building a parallel auth system.

Testing. In PHPUnit, bypass the HTTP login flow and mint tokens directly:

$token = JWTAuth::fromUser(User::factory()->create());
$this->withHeader('Authorization', "Bearer {$token}")
     ->getJson('/api/me')
     ->assertOk();

This keeps feature tests fast and independent of password hashing.

Integration tip — token transport. Clients send Authorization: Bearer <token>. If you also want cookie support for browsers, set the header from a cookie in a tiny middleware rather than teaching the guard about cookies.

Gotchas and Tips

  • The package is unmaintained (last release 2017). It was built for Laravel 5.x and PHP 5.6/7.0. On Laravel 8+/PHP 8 the provider bindings, facades, and middleware signatures may break outright. Treat a fresh install as a migration project and pin versions; for new work, seriously evaluate tymon/jwt-auth, Laravel Sanctum, or Passport instead.
  • secret is required and must be identical across nodes. A missing or per-node JWT_SECRET produces tokens that validate locally and fail behind a load balancer — the classic "works on my machine" auth bug. Set it in .env and never commit a fallback.
  • ttl is in minutes in this package's config. Read the published config file rather than assuming seconds; a 60 there means one hour, and clock skew between issuer and verifier can cause borderline 401s.
  • No revocation list out of the box. invalidate() rejects the presented token from being reused by your code, but there's no blacklist: a stolen unexpired token remains valid until exp. If you need immediate revocation (password change, logout-everywhere), keep short TTLs and add a jti/version column check in the guard, or move to a package with a blacklist.
  • Authorization header must be well-formed. Bearer with the wrong case, an empty value, or a proxy stripping the header yields "token not provided" rather than "invalid token" — differentiate those errors in your exception handler so client debugging isn't a guessing game.
  • Exception handling is on you. Expired vs. invalid vs. missing tokens should map to distinct responses; catch the package's token exceptions in app/Exceptions/Handler.php and return 401 JSON consistently instead of an HTML error page to an API client.
  • Don't put tokens in URLs. Query-string tokens leak in logs, Referer headers, and caches. Header-only.
  • Config caching. After changing config/jwt.php, run php artisan config:clear in dev; a cached config in production will silently ignore your .env change until config:cache is re-run.
  • CORS preflight. OPTIONS requests won't carry the Authorization header; exclude them from jwt.auth or the browser SPA will see failed preflights as auth failures.
  • Refresh windows. If you use the jwt.refresh middleware, ensure the refreshed token is returned to the client in a documented header/form — otherwise clients keep replaying the old token until it hard-expires.
  • Extension point. The guard resolves the user via your configured provider, so custom claims or a non-Eloquent user store can be handled in a custom provider; for anything more invasive (roles in claims, per-device tokens), subclassing the guard is cleaner than re-implementing auth from scratch.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource