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 Bundle Laravel Package

medzuch/jwt-bundle

medzuch/jwt-bundle is a Symfony bundle for JWT authentication, offering configurable services to issue, validate, and handle JSON Web Tokens in PHP applications. It streamlines token-based auth setup and integration with Symfony's service container.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What shipped. v1.2.0 is an interface-only release: a Refresh\ namespace containing RefreshTokenStoreInterface (four operations — store, consume, revokeGrant, revokeAllFor), a concrete RefreshTokenGenerator exposed as the Symfony service medzuch_jwt.refresh_token_generator, and the RefreshToken/RefreshTokenRecord value objects. No configuration keys, no services re-shaped, no migrations, no default storage implementation, no call sites. The library floor is unchanged at medzuch/jwt-php ^1.2.1.

Architecture fit. The package is a Symfony bundle, not a Laravel package — no service provider, no publishable config, no migrations, no facade. Consumed from a Laravel codebase it degrades to a plain Composer PHP library: the contracts and value objects are usable, the container wiring (medzuch_jwt.refresh_token_generator) is not, and would need a manual binding in a service provider. The design intent is explicit and worth respecting: the bundle owns vocabulary, not persistence. That is a defensible boundary (rotation, device records, and logout-everywhere are genuinely schema- and product-specific) but it means the package delivers the cheap half of the feature. The expensive half — the token table, the atomic conditional write, replay handling, expiry sweeps — is entirely ours.

Integration feasibility. Low friction to install, high friction to finish. Three things are genuinely well-designed and reduce our risk: consume() spends and reports a prior spend in one call, closing the find-then-delete race that causes double-use; RefreshToken derives its own SHA-256 so client value and stored hash cannot disagree; and the two revocations map cleanly to RFC 9700 §4.14.2 (lineage revocation on replay vs. revoke-all on password change). The fast-hash choice is correct for 256 bits of random_bytes() and avoids putting a slow KDF on the refresh hot path. That said, the interface is inert until we write a store, and the store is where the correctness risk lives.

Technical risk.

  • Interface stability. RefreshTokenStoreInterface is published at 1.x. Adding a fifth method later is a breaking change for every implementor. There is no stated deprecation policy for the interface itself; we should assume additive changes arrive as new interfaces, and confirm.
  • Replay semantics are ours to wire. alreadyUsed: true is reported, not acted on. Nothing revokes the lineage automatically. A store that returns the flag and moves on is a silent security hole.
  • No retention story. No purge/GC operation is in the contract and no cleanup is implied. Expired and revoked records accumulate indefinitely unless we build the sweep.
  • Retry-as-theft. Documented in the README, which is honest, but it means a lost 200 on the refresh response logs the user out. This needs client-side retry discipline we do not control.
  • Framework mismatch. Any Laravel-side evaluation must treat this as a library adoption, not a package adoption; framework-native alternatives (Sanctum/Passport) already own this ground.

Key questions for the maintainers. Is the interface intended to be frozen at four methods for the 1.x line? Is there a recommended purge/retention contract, even informally? Does consume() have a required atomicity guarantee we can hold them to (unique index on hash + conditional update), or is that purely implementor discretion? And for a Laravel consumer: is the generator constructible outside a Symfony container, or is the service definition the only supported entry point?

Integration Approach

Stack fit. Poor-to-moderate for a Laravel shop. We get two framework-agnostic value objects and a token generator we can construct manually; we do not get bundle auto-registration, config publishing, or any Laravel-idiomatic packaging. If the goal is refresh tokens in a Laravel app, Sanctum or Passport is the shorter path and carries the migrations we would otherwise write. If the goal is interoperability with sibling Symfony applications — the package's stated motivation — the vocabulary has real value. Decide which goal we have before adopting.

Migration path. Effectively zero-risk for existing consumers: nothing in configuration changes, no injected service changed shape, and an application that ignores the namespace upgrades by moving the constraint. UPGRADE.md is the whole story. For us there is nothing to migrate from; this is a greenfield adoption decision, not an upgrade.

Compatibility. PHP-only dependency chain via medzuch/jwt-php ^1.2.1; no framework version coupling introduced here. The Symfony-container coupling on the generator is the one portability gap. No Flex recipe will ship (DEC-9), which is the right call — Flex auto-registers bundles by package type, and a recipe's starter YAML would be a hand-maintained copy that drifts from the generated reference tree. The consequence for us is purely procedural: registration and setup come from jwt:key:generate plus the README quickstart, not from composer require alone.

Sequencing.

  1. Decide build-vs-adopt against framework-native alternatives; this release does not change that calculus, it only makes the contract explicit.
  2. If adopting, write a thin Laravel service provider binding RefreshTokenGenerator and our store implementation.
  3. Design the token table first: hash (unique, indexed), subject id (indexed for revoke-all), lineage/grant id (indexed for revokeGrant), issued/expiry/spent timestamps. This schema is the deliverable, not the interface.
  4. Implement consume() as a single conditional write; test it under concurrency before anything else.
  5. Wire replay handling: on alreadyUsed, call revokeGrant() and fail the request.
  6. Add a scheduled retention sweep before the first production deploy.
  7. Only then instrument refresh latency and store error rates.

Operational Impact

Maintenance. The package surface is small and the dependency floor is loose, so version churn should be minimal; expect to track a 1.x line with occasional additive contracts. Our real maintenance burden is the store implementation and its schema, which we own outright and which will outlive any particular version of this bundle. Budget for it as first-party code, not as glue.

Support. Documentation is the support channel: README, UPGRADE.md, and docs/plan.md §8.1 carrying the rationale since v0.4. That is unusually good for a small package, but it is documentation of intent, not an SLA. There is no default implementation to lean on when something misbehaves at 2 a.m., and no upstream code path to point at — the failure will be in our store.

Scaling. One indexed write per refresh, one read-and-write per consume, one indexed update per revocation. Cheap at any realistic session volume. The scaling concerns are index design (hash for lookup, subject for revoke-all, lineage for revokeGrant) and table growth without a sweep. A unique constraint on the hash is not optional — it is what makes the atomicity guarantee real. Refresh-path latency must stay flat; the fast-hash decision protects that, and we should not "improve" it to password_hash().

Failure modes.

  • Non-atomic consume() → the same token is spent twice; the flag exists but is never observed.
  • alreadyUsed reported and ignored → replay goes undetected, lineage lives on.
  • Lost refresh response + client retry → indistinguishable from theft → legitimate user force-logged-out.
  • No retention job → unbounded table growth and slower revoke-all scans.
  • Missing unique index on hash → race window reappears at the database layer despite a correct-looking interface implementation.
  • Empty/absent request parameter → returns null rather than throwing, which is the desired invalid_grant behavior but means silent misconfiguration looks like an auth failure, not a bug.

Ramp-up. Small and self-contained: four operations, two value objects, one generator, roughly a day for a competent engineer to read the README and UPGRADE.md and produce a first store. The higher cost is conceptual, not syntactic — the team must internalize why storage is out of scope, why consume() is one call, and why the two revocations are distinct. One reviewer with the RFC 9700 §4.14.2 context should own the store review; the code will look trivially correct while being wrong in the window it was designed to close.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager