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

roundly-consulting/jwt-for-laravel

JWT authentication for Laravel: issue, validate, and refresh JSON Web Tokens with configurable guards, middleware, and token handling that integrates cleanly into Laravel applications and APIs.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This release sharpens the package's position as a service-to-service authentication layer inside Laravel, not just a user-auth JWT library. The service-jwt guard, the per-issuer config (jwt.service.secrets, jwt.service.issuers, jwt.service.issuer), and HS256 signing describe a symmetric-secret trust mesh: each calling service signs with its own issuer identity, the receiving service validates against an allow-list of issuers and resolves the matching secret. For a TPM, that is a meaningful architectural statement — the package assumes services are known to each other and share provisioning, which is a good fit for a bounded internal platform (5–30 services, one platform team) and a poor fit for an open or partner-facing API surface.

The 1.2.0 additions are test-fidelity work, not runtime capability. Jwt::fake()->actingAsService($issuer, $claims, $audience) makes the fake mint a real HS256 token the way a foreign issuer would sign it — resolving that issuer's own jwt.service.secrets entry, falling back to the shared secret, and addressing the audience to the host service. Previously the fake retained the issuer it was constructed with, so cross-service tests either could not be expressed or were hand-encoded by developers. That hand-encoding is the real architectural liability this release removes: test tokens written by hand inevitably drift from the production signing and claim-assembly path, and the drift is silent until a test passes that should have failed.

forgetActing() is the more consequential fix, and it is worth flagging to stakeholders as a correctness improvement rather than a convenience. Because the fake re-attaches its bearer to every request lacking an Authorization header, withoutToken() (Laravel's standard anonymous-request helper) could not produce an unauthenticated request. Any test asserting "this endpoint rejects anonymous callers" was, in a faked context, potentially asserting nothing. Teams that adopted the fake widely should assume some of their negative-path auth coverage is weaker than it appears and re-verify it after upgrading.

Integration feasibility

Low. This is an additive minor release (1.2.0) on an existing public surface: two new methods on the object returned by Jwt::fake(), no signature changes, no config schema changes, no deprecations. Existing tests continue to compile and behave as before — including, importantly, the previously surprising withoutToken() behaviour, which is not silently corrected. That last point cuts both ways: upgrading is safe, but the fix only lands where a developer explicitly opts in by calling forgetActing(). There is no automated way to find tests that are currently under-asserting; a grep for withoutToken( inside faked test classes is the practical survey, and it will only find the tests someone thought to write.

The integration work is therefore concentrated in test suites, not production code. That is cheap to schedule and easy to scope, but it needs a deliberate sweep rather than a dependency bump, because the value is in the tests that don't exist yet.

Technical risk

  • Test-isolation semantics are newly dual. After 1.2.0, whether the fake leaks its bearer across a test's requests depends on the developer remembering forgetActing(). This is an improvement over having no escape hatch, but it introduces a stateful helper whose correct use is convention-based. Recommend a base test-case or trait that calls forgetActing() in tearDown() so the safe default is structural, not remembered.
  • Issuer/audience resolution is now exercised in tests, which may expose production config drift. actingAsService() resolves the issuer's secret from jwt.service.secrets and falls back to the shared secret. Teams that have only ever run against the shared secret may discover, once they mint for a foreign issuer, that their per-issuer entries are absent or wrong — a test-time discovery of a latent production misconfiguration. That is a good failure, but it will land as unplanned work.
  • The guard still rejects issuers absent from jwt.service.issuers. Good — the new method cannot be used to bypass the allow-list. Confirm this remains true and that a negative test for an unknown issuer exists alongside the new positive-path tests.
  • Symmetric secrets remain the core risk, unchanged by this release. HS256 means every validating service holds a secret capable of minting tokens for that issuer. There is no asymmetric option described here. This is acceptable inside a trust boundary and unacceptable across one. The release does not change this posture, and it should not be read as doing so.
  • Test-only API is now a compatibility surface. actingAsService() and forgetActing() take optional positional parameters ($claims, $audience). Once consumers depend on them, renaming or reordering becomes a breaking change in test code across every adopting team. Worth pinning and treating as public API in the semver contract.
  • Audience defaulting. When $audience is null, the token is addressed to "this service" implicitly. Convenient, but it means a test can pass without the author ever stating which audience it is testing. Ask for the default to be documented explicitly so reviewers know what a null-audience test actually proves.

Key questions

  1. Does the 1.2.0 test suite include a negative case proving actingAsService() with an issuer absent from jwt.service.issuers is still rejected by the guard?
  2. When $audience is null, is the minted aud the host service's configured identifier, and is that documented?
  3. Is forgetActing() safe to call in tearDown() unconditionally, including in tests that never used the fake?
  4. Does the per-issuer secret lookup fail loudly when an issuer has no jwt.service.secrets entry but the shared secret is also unset, or does it degrade to an unsigned/invalid token?
  5. Is there a stated deprecation or replacement path for the hand-encoded token helper that hosts built to work around the old issuer bug? Those helpers should be deleted, not left adjacent to the new method.

Integration Approach

Stack fit

Assumes a Laravel application on a supported PHP version with the package already installed for service-jwt guards. No new runtime dependencies are implied by the release notes — the additions are within the existing fake and signing path. Nothing here touches queue workers, scheduled jobs, or HTTP middleware registration, so the integration footprint is limited to the tests/ tree. Teams not using Jwt::fake() at all get no benefit and no risk from this release; teams using it heavily get the most value, which conveniently correlates with where the payoff is.

Migration path

Upgrade is a standard minor-version bump with no code changes required. The deliberate part is a two-track follow-up:

  1. Adopt the new capability. Replace hand-rolled service-token construction in tests with actingAsService($issuer, $claims, $audience). This is the primary reason to take the release: it collapses bespoke signing code into one path that the package maintains, and it is what makes cross-service authz tests expressible at all. Prioritise tests covering issuer allow-list enforcement, audience mismatch, and claim-based authorisation — the three places where hand-encoded tokens were most likely to be subtly wrong.
  2. Repair the anonymous-path gap. Sweep for tests that use withoutToken() or otherwise intend to assert unauthenticated behaviour while the fake is active, and add forgetActing() before the request. Where a test never asserted the anonymous case at all, add it — these endpoints are exactly the ones where a missing auth check would go unnoticed.

Do track 2 as a distinct work item with its own acceptance criteria, not as cleanup folded into track 1. Its output is evidence about production security posture, and it may surface endpoints with genuinely missing authorisation rather than merely missing tests. Route anything it finds through normal security triage rather than fixing it quietly in a test branch.

Compatibility

No breaking changes. The one compatibility subtlety is behavioural, not API: because withoutToken() still fails to produce an anonymous request in a faked context, a test that is upgraded but not annotated will keep passing for the wrong reason. There is no error, no deprecation notice, and no way to detect it from the upgrade itself. This is the reason the sweep must be explicit. Also verify that any internal base test case, trait, or package-provided test helper that wraps Jwt::fake() is updated to expose the two new methods — a wrapper that returns $this->fake but predates 1.2.0 will hide them.

Sequencing

  1. Pin to ^1.2 and bump in the shared dev dependency set so all services move together — divergence here produces test suites that behave differently per repo for the same code.
  2. Update the base test case to call forgetActing() in tearDown(), making the safe default structural before anyone starts writing new tests.
  3. Convert hand-encoded service tokens to actingAsService(), starting with the highest-value authz tests.
  4. Run the anonymous-path sweep and triage findings.
  5. Publish a short internal note with one before/after example of each method — this is a small, easily-missed API and adoption will otherwise stall at whoever read the changelog.

Operational Impact

Maintenance

Net reduction. Every hand-encoded token removed from the test tree is signing logic no longer maintained by the consuming team; the package now owns issuer selection, secret resolution, audience addressing, and claim merging. Expect a modest one-time cost to delete those helpers and the config duplication around them, and a small ongoing cost to track the fake's API as a compatibility surface. The tearDown() convention, once in the base case, is zero-maintenance.

Support

Low and largely self-inflicted if the anonymous-path gap is not addressed. Support burden splits cleanly: actingAsService() questions will be "which issuer secret did it pick" (answerable from config), while withoutToken() questions will be "why is my anonymous test authenticating" — a confusing symptom with a one-line fix that is hard to find without documentation. Pre-empt the second category with the internal note in step 5; it will pay for itself in the first week.

Scaling

Scales with the number of services in the trust mesh, not with traffic — this release is test-time only and adds nothing to the request path. What does scale is the configuration surface: one jwt.service.secrets entry and one jwt.service.issuers entry per counterpart, per environment. As the mesh grows, that becomes an N²-shaped provisioning problem and a rotation-coordination problem, and HS256 means every validator can mint. If the platform is heading past roughly a dozen services or crossing an organisational trust boundary, the right move is to evaluate asymmetric signing rather than to invest further in symmetric-secret tooling. Flag that as a forward-looking decision, not a defect in 1.2.0.

Failure modes

  • Silent under-assertion. Tests that appear to verify anonymous rejection but do not. Highest-severity mode because it masks a real auth gap and produces false confidence in exactly the security tests meant to catch it. Detect by sweep; fix with forgetActing().
  • Config drift exposed in tests. actingAsService() resolving a per-issuer secret that production has never actually configured. Surfaces as a failing test in an unrelated change; treat as a real config finding, not test flakiness.
  • Stateful fake leakage. actingAs() / actingAsService() persisting across requests or tests without forgetActing(), producing order-dependent suites. Mitigated by the tearDown() convention.
  • Stale hand-encoded helpers. Old workaround helpers left in place alongside the new method, quietly diverging from the package's signing path. Mitigated by deleting them as part of step 3, not merely deprecating them.
  • Issuer confusion. Two issuers whose secrets are misconfigured to the same value. Not introduced by this release, but now more easily testable — add a negative test per issuer pair.

Ramp-up

Roughly half a day for a team already using Jwt::fake(): read the changelog, add the tearDown() hook, convert a handful of tokens, run the sweep. Teams not using the fake should not adopt it solely for this release — the cost/benefit only works where service-auth tests already exist or are about to be written. One caveat worth stating plainly to engineering leadership: the sweep in step 4 may uncover endpoints lacking anonymous-rejection tests today. Budget for that discovery rather than presenting the upgrade as purely mechanical, because the finding is the point.

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.
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode