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.
Build vs. buy — the deciding input, resolved in one read. The package is a Symfony bundle (Bundle subclass, DI extension with a config tree, Security firewall authenticator). For a Laravel codebase there is no "buy" to weigh: the runtime leverage is zero. The decision it actually drives is to budget a Laravel-native path — laravel/sanctum for first-party SPA/token auth, laravel/passport if you genuinely need OAuth2, or a hand-rolled firebase/php-jwt guard if you want the JWT shape without the framework. That reframing is the highest-value output of this evaluation and it takes ten minutes, not a sprint.
Roadmap reality check (do this before any spike). The package's entire premise is "Symfony service in the estate." If Symfony is not in scope today and not credibly on the 12-month roadmap, this is a wrong-framework evaluation and should be closed, not spiked. If Symfony is arriving — an acquisition, a vendor integration, a service being rewritten — this becomes a real (though not preferred) candidate and the evaluation reopens on different terms: compare it against lexik/jwt-authentication-bundle, not against nothing.
A cost baseline any alternative must beat. A faithful Laravel port/wrap — ServiceProvider, custom guard, middleware, claims mapping, and tests that fake Symfony container/firewall internals — sizes at roughly 3–5 engineer-weeks. That number is the benchmark. Sanctum beats it inside a day; a maintained third-party JWT package beats it inside a week. Writing the number down is what stops the "we could just port it" conversation from recurring in every auth-adjacent planning session.
Requirement discovery, delivered free. Enumerating what this bundle does not evidence gives you a concrete acceptance checklist for whatever you do adopt: refresh-token rotation, revocation/denylist keyed on jti, key rotation with a kid, OAuth2/OIDC, multi-tenant claim strategy, asymmetric signing (RS256/EdDSA), clock-skew leeway. These are auth requirements most teams discover as incidents. Pulling them forward into the epic's definition-of-done is a direct quality gain with no dependency cost.
A spec for our own implementation, at zero license cost. The portable patterns — a TokenIssuer/TokenParser service boundary that keeps JWT primitives out of controllers, claims as a namespaced envelope, short access TTL with refresh stored opaquely outside the JWT layer, and the extract→verify→load-subject→set-context→authorize sequence — are worth roughly half a day of PM/tech-lead writing to capture as an internal design note. That note is what you hand the engineer who builds the Laravel guard, and it outlives whichever package wins.
Risk-register entry and a dependency-policy precedent. 0 stars, 0 dependents, no issue history, single maintainer, and a release tagged 2026-08-01 — a future-dated tag is a metadata artifact that must be verified before any lockfile resolves it. Logging this as a formal "we evaluated and declined" record gives you a reusable bar for the highest-blast-radius component you ship: adoption signals, maintenance ownership, security-patch path. Apply the same bar retroactively to your existing auth dependencies — that audit is likely worth more than the package under review.
Prioritization signal for the auth epic. Auth is usually under-resourced because it looks like plumbing. A written evaluation that names refresh rotation, revocation, and rotation tooling as out of scope of the free option is an effective way to get those items staffed rather than deferred to "phase 2."
Explicitly not supported by this evaluation: decisions about Laravel services, platform-level SSO, regulated identity workloads, or anything requiring a vendor SLA. Do not let this document be cited as due diligence on those.
Consider it — narrowly — only when all of the following hold:
config/bundles.php and firewalls in active use. Not "Symfony-ish," not a Laravel app with Symfony components pulled in transitively.firebase/php-jwt upstream constraint for CVEs, and hold the rotation/revocation story.2026-08-01 release date against the actual tag and commit history.Look elsewhere — and say so plainly — when:
Illuminate\Auth\Guard, no middleware contract; the guard/IoC/firewall models do not map. Porting is net-new auth work wearing this package's name, and you'd be rebuilding Symfony's container semantics by hand.lexik/jwt-authentication-bundle is the de facto standard — same framework fit, materially better docs, real adoption, and an issue history you can search. Preferring an unproven bundle here has no upside.kid, or OAuth2/OIDC is required. Building those yourself erases the entire buy rationale — at that point you're writing an auth library with a bundle as a starting template.Gating questions to answer before any spike is scheduled: Is Symfony actually in our stack? Who owns the fork, by name, with hours allocated? What is our revocation requirement, and is it fail-open or fail-closed when the denylist is unavailable? Is the 2026 release a real tag or metadata noise? If any of those has no answer, the evaluation closes.
For executives — the two-paragraph version. "Our evaluation found this package is Symfony-only, so it does not shorten our Laravel path — the 'buy' option simply isn't available to us here. I'm recommending we not adopt it and instead standardize on a Laravel-native auth package (Sanctum or equivalent), which keeps us on a supported, documented path at the same or lower cost. The useful output is a benchmark: we now know what a JWT bundle must cover, what a port would have cost us (3–5 engineer-weeks), and why we're not paying it. Net: no added spend, no added maintenance liability, and a documented decision we can point to if the question comes back."
Pair that with the risk framing: "This would have been a single-maintainer dependency in our authentication layer, at zero stars and zero dependents, with a release date that needs verification. We declined on maintenance risk before we got to the port cost." That sentence is reusable for any future "should we just use this small library" question, and it is the part leadership will remember.
For engineering — the actionable brief.
composer.json and the Bundle class and move on; that's a 15-minute verification, not a spike.src/DependencyInjection/*Extension.php and src/Security/. Roughly 30–60 minutes. The config tree is the public API, and the authenticator is the integration surface. You're harvesting structure, not code — generated READMEs describe intent, not signatures.TokenIssuer, one TokenParser, JWT primitives confined to those two classes, controllers and middleware never touching JWT::decode. That boundary makes the eventual package choice a one-line container change.alg: none in a test, 30–60s clock-skew leeway on nbf/exp, 5–15 minute access TTL with refresh tokens in an opaque store you control, and a route-list smoke test asserting 401-without-token across every protected route.ExpiredException vs. SignatureInvalidException vs. BeforeValidException is your incident triage in one line.The one-line summary for the roadmap review: Evaluated and declined — wrong framework for us, and a maintenance profile we won't accept in the auth layer. Captured the design patterns and the requirement checklist; proceeding with a Laravel-native guard behind a swappable token-service interface.
How can I help you explore Laravel packages today?