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

Jose Bundle Laravel Package

spomky-labs/jose-bundle

Symfony bundle integrating spomky-labs/jose for JSON Object Signing and Encryption. Provides service configuration for JWS, JWE, JWK, and JWT handling, helping Symfony applications sign, verify, encrypt, and decrypt tokens and keys through the container.

View on GitHub
Deep Wiki
Context7

Getting Started

v2.3.0 is a compatibility release, not a feature release. Nothing new to learn, but the PHP floor moved and the underlying JOSE library's major version moved with it. Read the diff before you composer update.

What actually changed:

Change Practical impact
Symfony 4 support Autowiring-friendly; container definitions reworked. If you're bridging the bundle's service definitions into Laravel, re-check the XML/YAML service files — argument names and tags may have moved.
spomky-labs/jose 6.1 or 7.0 This is the part that will bite you. The 5.x → 6.x/7.x jump relocated namespaces and factory methods. Any snippet you copied from a pre-2018 blog post is dead.
PHP 5.6 / HHVM tests removed Effective floor is PHP 7.2+. Realistically that means Laravel 5.6+ (5.6 is the first release testing on 7.2).

Where to look first, in order:

  1. composer.json — confirm the spomky-labs/jose constraint and whether symfony/framework-bundle is a hard or soft requirement.
  2. DependencyInjection/Configuration.php — the full option surface, and your map for a Laravel config file.
  3. The jose library's JWKSet / signer / verifier entry points, pinned to the version you actually installed, not master.
  4. CHANGELOG.md / UPGRADE notes for the jose major bump specifically.

Minimal first use case — verify a third-party RS256 id_token (an OIDC provider, or a Laravel Passport instance elsewhere):

composer require spomky-labs/jose:^7.0
// config/jose.php
return [
    'jwks_uri' => env('OIDC_JWKS_URI'),
    'algorithms' => ['RS256'],
    'leeway' => 30,
];
$jwkSet = $this->jwkSetRepository->get(config('jose.jwks_uri')); // cached
$token   = $jwkSet->getKey($header['kid'])->verify($jws);           // pin alg first

Start with verification only. Signing your own tokens is the second step, and the one where you're more likely to ship an alg: none hole.

Implementation Patterns

Don't install the bundle — install the library. In a Laravel app the bundle is dead weight: it drags in symfony/config, symfony/dependency-injection and symfony/framework-bundle for a container you don't use. Pull spomky-labs/jose directly and hand-wire the three or four objects you need. Only use the bundle if you're sharing code with a Symfony app.

One service provider, three singletons. Wrap construction in a provider so keys aren't rebuilt per resolution:

$this->app->singleton(JWKSet::class, fn () => $this->loadKeySet());
$this->app->singleton(Signer::class,  fn ($app) => new Signer($app->make(JWKSet::class)));
$this->app->singleton(Verifier::class, fn ($app) => new Verifier($app->make(JWKSet::class)));

Bind them as interfaces you own (TokenSigner, TokenVerifier) so tests and future library swaps don't touch call sites.

Symfony config → Laravel config. The bundle's Configuration.php gives you the shape; flatten it into config/jose.php with env() at the leaves. Keep algorithm lists, key material references, and leeway in config — not in the signer class.

Middleware for bearer auth. A VerifyJwt middleware that: extracts the token, parses the header, asserts alg ∈ allowlist, resolves the key by kid, verifies, then merges claims into the request. Fail closed on every branch. Register it on the API group, not globally.

Key-set resolution by kid, with rotation support. Cache the JWK set under a key derived from the issuer URL. On key-not-found for a known kid, force a single re-fetch (the provider rotated) and retry once — never loop.

Multi-issuer / multi-tenant. Resolve the verifier from the iss claim via a registry map rather than branching inline. Each tenant gets its own JWK set and algorithm allowlist.

Per-algorithm separation. Keep HS256 (shared secret, internal service-to-service) in a different code path from RS256/ES256 (public key, third-party). Mixing them in one verifier is how algorithm-confusion bugs get shipped.

Testing. Ship a fixture JWK set, freeze the clock (Carbon::setTestNow()) for exp/nbf assertions, and write at least one test per rejection reason: expired, not-yet-valid, wrong aud, wrong iss, bad signature, unknown kid. The happy path is the least interesting test here.

Gotchas and Tips

The jose bump is the breaking change, not Symfony 4. 6.1 → 7.0 relocates classes and changes factory signatures. If you use the bundle's facades you may not notice; the moment you touch the library directly you will. Read the library's own upgrade notes, not the bundle's brief changelog line.

PHP floor is now 7.2. Pinned to PHP 7.1, Laravel 5.5, or an old Docker base image? Stay on the prior release — 2.3.0 won't install and won't be tested for you. Update the CI matrix in the same PR; dropping HHVM/5.6 from upstream CI means those paths are genuinely unverified now.

ext-gmp or ext-bcmath is effectively required for RSA/EC work. Missing it fails at runtime with an opaque math error, not at install time. Check php -m before debugging signatures.

PEM in env vars is a classic. Multi-line keys in .env lose their newlines or get quote-stripped. Store a path (JWT_PRIVATE_KEY_PATH) or base64 the PEM; convert to JWK once at boot.

Base64url, not base64. No padding, -/_ instead of +//. A token that "verifies locally but not in prod" is usually a transport layer re-encoding.

Pin the algorithm allowlist explicitly and reject none. Pass the expected algorithm into verification rather than trusting the token header. Also compare aud and iss — signature validity alone is not authorization.

Cache the JWKS aggressively but bound it. Network fetch per request will take your API down when the provider hiccups. Cache 1h, and treat the re-fetch-on-unknown-kid path as rate-limited.

Clock skew. Default leeway of 0 breaks the moment a signer's clock drifts a few seconds. Set 30–60s. Conversely, don't set 300s to paper over a broken NTP setup.

Debugging order. On failure, log the header first (alg, kid, typ) — nine times out of ten it's a kid you don't have or an alg you didn't allowlist. Then the exception type. The library's exception hierarchy is fine-grained; catch the specific ones and map them to 401 vs 500, don't catch \Exception.

Extension point. If you outgrow the single-key setup, the clean seam is a KeyResolver contract you define, with the library's JWKSet as one implementation — that's how you add DB-backed keys, per-tenant sets, or a KMS without rewriting the middleware.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core