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

Jwt Auth Bundle Laravel Package

dimkinthepro/jwt-auth-bundle

A JWT authentication bundle for PHP apps that handles token issuance, validation and request guards. Configure signing keys, token TTL and refresh behaviour, then protect routes with drop-in middleware and a simple, framework-friendly API.

View on GitHub
Deep Wiki
Context7

Getting Started

Run the framework check before you install anything. This is a Symfony Bundle — it ships a Bundle subclass, a DependencyInjection/*Extension with a config tree, and a Security firewall factory. There is no ServiceProvider, no publishes(), no Illuminate\Auth\Guard, no middleware contract. On a Laravel app the runtime leverage is zero; the realistic day-to-day value is as a reference design you read once (30–60 minutes) and reimplement with firebase/php-jwt. Confirm which situation you're in before spending a single day on integration.

Read these four things, in this order, before trusting any README:

  1. composer.json — PHP/Symfony constraints and, critically, the firebase/php-jwt constraint. That tells you what's underneath and whether upstream security patches reach you.
  2. src/DependencyInjection/*Extension.php — the config tree is the public API. Node names, defaults, and required keys live here and are more reliable than prose.
  3. src/Security/ — the authenticator/listener and the firewall key it registers. This is the integration surface.
  4. The Bundle class — service registration, autowiring aliases, compiler passes.

Skip the generated summaries until you've done that; they describe intent, not signatures.

First use case (Symfony — only if that is genuinely your stack): enable the bundle in config/bundles.php, add the config block (signing key, algorithm, TTL, user provider), attach the authenticator to your API firewall with stateless: true, then hit one protected route with a Bearer token and confirm the profiler's security panel shows your authenticator running. Verify both a 401 (no/garbage token) and a 200 before touching the rest of the API surface.

First use case (Laravel — the realistic one): don't install it. Read the token service boundary and the claim envelope, then build that shape yourself. One POST /auth/token endpoint, one auth:api route, and the JWT logic behind a class you can swap out.

Before it enters any lockfile: verify the 2026-08-01 last-release date. A future-dated tag is a metadata artifact you do not want composer update resolving silently.

Implementation Patterns

Token service boundary — copy this regardless of framework. Controllers and middleware never touch JWT primitives. One service issues, one validates, both injected:

interface TokenIssuer { public function issue(Authenticatable $user, array $extra = []): string; }
interface TokenParser { public function parse(string $token): Claims; } // throws on invalid

In Symfony this is the bundle's core service (confirm the concrete class in services.yaml). In Laravel you write ~80 lines around firebase/php-jwt. Either way the app binds to the interfaces, so swapping the implementation later is a container/config change, not a refactor.

Claims as an extensible envelope. Baseline sub, iat, nbf, exp, jti, plus an application namespace (tenant_id, roles, scope). Decide up front whether authorization data rides in the token (fast, stale until expiry) or is re-fetched per request (slower, revocable). For anything role-bearing, re-fetch — otherwise a demoted admin keeps their powers for the full TTL.

Short access token; refresh lives outside the JWT library. The bundle gives you issuance, signing config, and TTL. It does not give you rotation. Treat the access token as 5–15 minutes and keep refresh tokens in an opaque, DB/Redis-backed store you control. This is the decision that determines whether your auth is recoverable after an incident.

Listener vs. middleware — port the sequence, not the code. Symfony's idiom is a request listener/authenticator; Laravel's is middleware. The concepts map cleanly, the implementations don't. Keep the order: extract bearer header → parse/verify → load subject → build auth context → set on request → let the framework's authorization layer decide. Contract-test each step independently.

Guard wiring in Laravel, with firebase/php-jwt directly:

// app/Auth/JwtGuard.php
public function user() {
    if ($this->user !== null) return $this->user;
    $raw = $this->request->bearerToken();
    if (!$raw) return null;
    try {
        $claims = JWT::decode($raw, new Key(config('jwt.secret'), config('jwt.algo')));
    } catch (\UnexpectedValueException|\ExpiredException $e) {
        return null;
    }
    return $this->user = $this->provider->retrieveById($claims->sub);
}

Register under config/auth.php → guards, add an auth:api middleware alias. That's the entire "port": one guard, one config file, one alias.

Config parity for reversibility. Mirror the bundle's node names in config/jwt.php (secret, algo, ttl, user_provider). If a Symfony service ever appears in the estate — or you migrate toward one — the semantics line up and the diff is mechanical.

Time-frozen tests. Every JWT bug is a clock bug. Inject a clock, freeze it, and table-drive the failure matrix: expired, nbf in the future, wrong algorithm, tampered payload, missing claim, valid. Six tests catch ~90% of auth regressions. Add a route-list smoke test asserting 401-without-token across every protected route — that's what catches silent bypasses.

Gotchas and Tips

  • Wrong framework is the headline. Every hour wiring this into Laravel is an hour rebuilding Symfony's container, event dispatcher, and firewall semantics — with no vendor, no issue history, and no Stack Overflow corpus. You are the support tier.
  • Docs are generated summaries, not verified API docs. Methods like refreshToken() or custom-claim constructors may not exist as described. Trust src/. Before coding against a name you read in a README, run php bin/console debug:config <bundle_alias> and debug:container --tag=security.authenticator.
  • No refresh-token rotation, no revocation, no OAuth2/OIDC. Missing revocation isn't a detail — it means "log out everywhere," role changes, and compromised-token response are unsolved until you add a Redis denylist keyed on jti. Decide fail-open vs. fail-closed in writing: the default when Redis is down is an auth outage, and that should be a decision, not a 2 a.m. discovery.
  • Algorithm confusion is the classic JWT CVE. Pin the algorithm in config, reject none, never derive it from the token header. With firebase/php-jwt, pass the expected algorithm explicitly — behaviour differs by version — and assert it with a test that feeds an alg: none token and expects rejection.
  • Clock skew. Allow 30–60s leeway on nbf/exp. Distributed nodes with minor drift will otherwise reject valid tokens intermittently — the worst class of bug, because it reads as flaky infrastructure.
  • Key rotation without kid is a coordinated outage. Verify in source whether the bundle emits a key identifier and supports overlapping keys. If not, rotating means validating against two keys during a window — budget short TTLs so the exposure is bounded.
  • Stateless means no server-side logout. The browser drops the token; the token stays valid until expiry. Design the session UX around short TTL + refresh, and assume a stolen token works for its full lifetime.
  • Symfony version coupling. The authenticator system changed materially in 5.3; firewall config shape is version-sensitive. Pin the Symfony minor and expect breakage on majors with no upstream fix coming.
  • Debugging toolkit. Symfony: profiler security panel, debug:config, debug:firewall <name>, debug:container --tag=security.authenticator. Laravel adaptation: log the decode exception class, not just the message — ExpiredException vs SignatureInvalidException vs BeforeValidException separates routine expiry from attack from clock skew, which is your whole incident triage in one line.
  • Ownership is the real extension point. Custom claim providers, user providers, and event subscribers exist — but at 0 stars and 0 dependents, you maintain every one you adopt. Fork into a path repository with a NOTICE, pin the firebase/php-jwt version, and put a CVE-monitoring reminder on the calendar. If nobody is funded to own that, the honest move is to close the evaluation and standardize on a maintained Laravel-native option.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator