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.
service-jwt guards, issuer allowlists, per-issuer secrets) as a sibling of user auth. 1.2.0 makes that surface testable, which is the gate for deciding whether to standardize internal service auth on this package rather than a bespoke middleware or an external IdP.Jwt::fake()->actingAsService($issuer, $claims, $audience) mints a real HS256 token signed the way that issuer signs it (own jwt.service.secrets entry, else shared secret) and addressed to this service. Previously teams hand-encoded tokens or fell back to a real issuer, so the "cost of the in-house alternative" was quietly a pile of test fixtures. That cost now goes to zero, which materially shifts the buy case.jwt.service.issuers, you can write acceptance tests for the "an unexpected caller is refused" path. That's a security requirement you can demo to a customer or auditor without standing up a second real service.forgetActing() restores anonymity after actingAs() / actingAsService(). Laravel's withoutToken() couldn't, because the fake re-attaches its bearer to any request lacking an Authorization header. This is the kind of gap that produces "works alone, fails in the suite" flakes — worth tracking as an engineering-health item, not a feature.Adopt when:
actingAsService(), and rejected callers via a foreign or unlisted issuer.actingAs(), Jwt::fake()) and want service identity to feel the same to engineers.jwt.service.secrets) rather than one global shared secret, and you want that exercised in tests.Look elsewhere when:
actingAsService() mirrors production, which is honest, but it also means the shared secret is a real part of your trust model.Migration note: this is an additive minor (1.2.0). App-issued outbound tokens deliberately keep their own issuer — the change is scoped to the test fake — so no production behavior changes and no coordinated rollout is needed. Existing hand-rolled token fixtures can be replaced incrementally.
To executives: This release removes a recurring tax on our test suites. Testing anything that involves one service calling another used to mean hand-building fake auth tokens — brittle, easy to get subtly wrong, and a common source of flaky builds. Two new test helpers let engineers simulate a trusted calling service in one line, and just as importantly, simulate an untrusted one to prove we reject it. It's a small, additive change with no production impact, but it lowers the cost of the security tests we're already committed to and shortens the path for new engineers touching service auth.
To engineering: Jwt::fake()->actingAsService($issuer, $claims, $audience) mints a genuine HS256 token as that issuer would sign it — using the issuer's own jwt.service.secrets entry if one exists, otherwise the shared secret — and addressed to this service. The fake's issuer is no longer pinned to jwt.service.issuer, so you can mint for a foreign issuer in tests; the guard still enforces the jwt.service.issuers allowlist, which is exactly the asymmetry you want. Your app's outbound tokens are unchanged and keep their configured issuer. forgetActing() clears the fake identity for subsequent requests — necessary because withoutToken() doesn't work against this fake, since it re-attaches a bearer to any request without an Authorization header. Add forgetActing() to tearDown or between scenarios rather than relying on withoutToken().
To security/compliance: The release makes two previously untested paths testable: the positive case (a legitimate caller with correct issuer and audience) and the negative case (a caller with an issuer outside the allowlist is still rejected). It does not weaken the trust model — the allowlist check remains in the guard, and the shared-secret fallback in the fake mirrors production behavior rather than inventing a new bypass. Recommend we add regression tests for bad-issuer and wrong-audience rejection as part of adoption.
How can I help you explore Laravel packages today?