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

Lexik Jose Bridge Laravel Package

spomky-labs/lexik-jose-bridge

Integrates LexikJWTAuthenticationBundle with the Spomky-Labs JWT Framework, letting Symfony apps create and verify JWTs using JWT Framework services, key management, and configurable signing algorithms.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: spomky-labs/lexik-jose-bridge is a Symfony integration package. It wires the web-token/jwt-framework (Jose) components into lexik/jwt-authentication-bundle via DI configuration. In a Laravel app you almost never install the bridge itself — you consume the underlying Jose library directly and hand-wire it. The bridge's README and config tree are still the best reference for how the pieces fit together.

Practical starting points:

# What you actually install in Laravel
composer require web-token/jwt-library:^3.3

# What the release notes are about (Symfony only — do not require in Laravel)
# composer require spomky-labs/lexik-jose-bridge:^4.0

Where to look first:

  1. composer.json of the bridge — it pins the exact web-token/* and symfony/* ranges that v4.0.0 validates against. Copy those constraints for your own install.
  2. The bridge's DependencyInjection/ folder — it shows every Jose service (JWSBuilder, JWSVerifier, JWKFactory, HeaderCheckerManager, ClaimCheckerManager) and the constructor arguments each needs. That is your service-provider blueprint.
  3. The new installation docs (PRs #65/#66) — the config recipes map 1:1 onto a Laravel config file.

First use case: sign and verify your own JWTs (API tokens, service-to-service auth, webhook signatures) with RS256 and a keyset you control, rather than delegating to tymon/jwt-auth or firebase/php-jwt.

// app/Providers/JoseServiceProvider.php
public function register(): void
{
    $this->app->singleton(AlgorithmManager::class, fn () => new AlgorithmManager([
        new RS256(),
    ]));

    $this->app->singleton(JWSBuilder::class, fn ($app) => new JWSBuilder(
        $app->make(AlgorithmManager::class)
    ));
}

Implementation Patterns

Signing with an asymmetric key. Keep the private key outside the repo (storage/keys/private.pem, chmod 600), load once, cache the JWK object.

$jwk = JWKFactory::createFromKeyFile(
    storage_path('keys/private.pem'),
    null,
    ['use' => 'sig', 'alg' => 'RS256', 'kid' => config('jose.kid')]
);

$jws = $builder
    ->create(['alg' => 'RS256', 'kid' => $jwk->get('kid')])
    ->withPayload(json_encode($claims, JSON_THROW_ON_ERROR))
    ->build();

$jws = $signer->sign($jws, $jwk, 0);

return (new CompactSerializer())->serialize($jws, 0);

Verification pipeline. Never call the verifier bare. Build a JWSLoader with a header checker (pins alg + kid) and a claim checker (enforces exp, nbf, iat, aud, iss). This is exactly the composition the bridge performs for Lexik.

$headerChecker = new HeaderCheckerManager(
    [new AlgorithmChecker(['RS256']), new TokenTypeChecker(['JWT'])],
    [new AlgorithmParameterManager()]
);

$claimChecker = new ClaimCheckerManager([
    new ExpirationTimeChecker($clockSkew = 30),
    new IssuedAtChecker($clockSkew),
    new NotBeforeChecker($clockSkew),
    new AudienceChecker(config('jose.audience')),
]);

Key rotation via JWKS. Publish /.well-known/jwks.json from a JWKSet, include kid in every token, and let verifiers select by kid. Consumers should Cache::remember() the JWKS for minutes, not hours, and re-fetch once on an unknown kid before failing.

Multi-tenant / per-issuer keys. Bind a small resolver class that maps iss → JWKSet, and inject it into the loader. This mirrors the bridge's key_loader / signature_algorithm config keys without the Symfony DI baggage.

Middleware integration. Wrap verification in an AuthenticateJoseToken middleware: pull the Bearer token, verify, stash claims on the request ($request->attributes->set('jose.claims', $claims)), and let a policy/gate consume them. Throw AuthenticationException so Laravel renders 401 consistently.

Outbound calls. For service-to-service, put signing in a JoseClient decorator around Illuminate\Http\Client\PendingRequest so every request gets a short-lived (~60s) Authorization: Bearer header, and verify the counterpart's JWKS on responses if signed.

Testing. Jose uses wall-clock time internally — you cannot freeze it. In feature tests, mint tokens with explicit iat/exp relative to now() and assert on rejection windows (exp = now-1) rather than mocking clocks.

Gotchas and Tips

  • Do not composer require the bridge in Laravel. v4 pulls lexik/jwt-authentication-bundle and Symfony 6 components. It will resolve, but you inherit bundle config, Flex recipes, and a service container you don't use. Take the library, not the bridge.
  • v4.0.0 is a hard break. PHP 8.1+ and Symfony 6.x only. If any transitive dependency still requires symfony/*:^5.4 (common in older packages), Composer will deadlock on the bridge. Your Laravel app is unaffected if you only install web-token/jwt-library, but pin your symfony/* ranges if you do pull it in.
  • v4 has no functional changes. The release is tooling and docs: ECS→PHP-CS-FIXER (#62), Rector (#63), PHPStan (#64), installation docs (#65), Composer/recipe docs (#66). So do not expect bug fixes or new features — upgrade only for the PHP/Symfony floor.
  • Algorithm confusion is the classic JWT vulnerability. Pass an explicit allow-list to AlgorithmManager and AlgorithmChecker. Never let the token's alg header select the algorithm, and reject none outright.
  • Clock skew is real. Default ExpirationTimeChecker has zero drift. Give 30–60s of tolerance or you will get spurious 401s across containers with slightly skewed clocks.
  • Empty claim sets serialize as [], not {}. json_encode([]) produces a JSON array, which violates the JWT spec's object requirement. Cast to (object) or use new \ArrayObject([]).
  • PEM format matters. JWKFactory::createFromKeyFile() wants PKCS#1/PKCS#8 PEM. Keys emitted by modern openssl genpkey are fine; .p12/.pfx bundles need createFromPKCS12CertificateFile() and a password argument.
  • web-token/jwt-framework vs web-token/jwt-library. The framework package includes the Symfony bundle; installing both causes duplicate class maps. In Laravel, install only jwt-library (or the individual jwt-signature / jwt-key-management components).
  • Debugging signed tokens. Decode with CompactSerializer::unserialize() and inspect $jws->getSignature(0)->getProtectedHeader() — the kid and alg there are what your header checker is validating. Log the kid on every verification failure; mismatched kid is the single most common cause of "valid token rejected".
  • JWKS cache invalidation. If you cache the remote keyset, cache the failure path too — retry the fetch once on an unknown kid, then cache the new set. Otherwise key rotation causes a hard outage until your TTL expires.
  • Bridge as documentation, not dependency. The DependencyInjection/Configuration.php tree in the bridge is an excellent specification of sane defaults for each Jose service. Read it, replicate it in config/jose.php, and skip the bundle.
  • If you only need HS256, firebase/php-jwt is a tenth of the weight. Reach for Jose when you need JWK/JWKS, key rotation, kid-based selection, or multiple algorithms — that is where web-token earns its complexity.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource