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

Php Jwk Laravel Package

strobotti/php-jwk

A lightweight PHP library for JSON Web Keys (JWK/JWKS). Parse, validate, and generate RSA and EC keys, build key sets, and integrate with JWT signing and encryption workflows. Framework-agnostic, Composer-ready, and useful for securely managing public/private keys in PHP applications.

Deep Wiki
Context7

Getting Started

Install and requirements

composer require strobotti/php-jwk

The package depends on ext-openssl and a modern PHP (pin to ^1.x for PHP 7.1–7.4 projects, ^2.x for PHP 8+). There is no framework integration, no service provider, no facade — it is a small value-object library, so expect to wire it into your app yourself.

Where to look first

Open the JWKFactory class in src/. Every object you create will come from there — JWKRSA, JWKEC, JWKOct, and JWKSet are almost never instantiated by hand. Skim the four classes and the README; the whole surface area is about a dozen methods.

First useful use case

You already have an RSA private key on disk and you need to publish a JWKS for token consumers:

use Strobotti\JWK\JWKFactory;
use Strobotti\JWK\JWKSet;

$key = JWKFactory::createFromKeyFile(
    storage_path('keys/app.pem'),
    null,                     // passphrase, or null
    ['kid' => 'app-2024-01', 'alg' => 'RS256', 'use' => 'sig']
);

$set = new JWKSet();
$set->addKey($key);

file_put_contents(public_path('.well-known/jwks.json'), $set->toJSON());

That single snippet covers 80% of real-world usage: load a PEM, attach metadata, serialize to JSON, serve it.

Implementation Patterns

Wrap the factory in a small loader service. Read the key path, passphrase, and kid from config, call JWKFactory::createFromKeyFile(), and cache the resulting object (or the JSON string) for the request or in Laravel's cache. OpenSSL file parsing on every request is wasteful:

class JwkProvider
{
    public function signingKey(): array
    {
        return cache()->rememberForever('jwk.sig', fn () => JWKFactory::createFromKeyFile(
            config('jwt.private_key_path'),
            config('jwt.private_key_passphrase'),
            ['kid' => config('jwt.kid'), 'alg' => 'RS256', 'use' => 'sig']
        )->toJSON());
    }
}

Use toPEM() as the bridge to JWT libraries. php-jwk does not sign or verify tokens. For firebase/php-jwt or lcobucci/jwt, the workflow is: build a JWK, then hand $jwk->toPEM() to the library as your PEM string. This lets you drive key selection from kid in a JWKS while still using whichever JWT library your app already has.

Serve a JWKS endpoint from JWKSet. $set->addKey($key) is chainable in spirit but not actually fluent — call it in sequence. Publish only the public components; a JWK built from a private PEM will serialize the private parameters (d, p, q, dp, dq, qi) into the JSON. Either build from the public PEM, or read the n/e fields out and construct a public-only JWKSet for output.

Parse inbound JWKS with JWKSet::createFromJSON(). When verifying tokens issued by a third party (Auth0, Google, Keycloak), fetch their JWKS URL, decode it, iterate getKeys(), match on getKeyID() against the token header's kid, then call toPEM() and pass it to your verifier. Wrap the fetch in a cache — JWKS documents rarely change but must be refreshable on kid miss.

Generate keys for tests and tooling. For integration tests around RS256/HS256 flows, use JWKFactory::createRSAKey(2048) or JWKFactory::createOctKey(256) in setUp(). It saves you from shipping fixture PEMs and keeps the test self-contained.

Set metadata explicitly. setKeyID(), setAlgorithm(), and setUse() (or the equivalent array keys) are not decoration — many verifiers reject tokens whose kid cannot be matched, and some refuse keys without alg. Always set kid on keys you publish, and set alg to match your signing algorithm exactly (e.g. RS256, not RS384).

Symmetric keys for HS256. JWKFactory::createOctKey() produces an oct JWK whose k member is the base64url-encoded secret. If you already have a shared secret, build the JWK via JWKFactory::createFromValues(['kty' => 'oct', 'k' => base64url_encode($secret)]) rather than generating a new one — otherwise your existing tokens will not verify.

Gotchas and Tips

The library never signs or verifies. It is a serializer/deserializer for key material. All cryptographic operations come from OpenSSL and whatever JWT library you pair it with. Debugging "token rejected" almost always lies in the JWT layer, not here.

Beware publishing private keys. JWKFactory::createFromKeyFile() on a private PEM produces a JWK containing both public and private members. If you blindly toJSON() it into .well-known/jwks.json, you have leaked your signing key. Sanity-check the output for the d member before committing a JWKS fixture.

Base64url, not base64. All members use URL-safe base64 without padding. If you hand-build a JWK from raw key components, encode with rtrim(strtr(base64_encode($bin), '+/', '-_'), '='). Feeding standard base64 produces keys that parse in this library but fail in strict verifiers.

createFromKeyFile() throws on the usual suspects: missing file, wrong passphrase, unreadable EC curve. Catch and log the OpenSSL error string — the exception message is terse and the underlying reason is often only in openssl_error_string().

EC support is narrower than RSA. JWKEC goes through OpenSSL, so only curves your build supports (P-256, P-384, P-521 typically) work. If you need secp256k1 or Ed25519, this package is the wrong tool — reach for web-token/jwt-framework or firebase/php-jwt with native key handling.

Extra header fields collide with standard ones. The "additional values" array passed to the factory merges into the JWK. Setting 'alg' => 'RS256' is fine, but misspellings ('algorithm', 'key_id') are silently dumped into the output as unknown members. Some validators will reject JWKs with unrecognized members; others ignore them. Verify the JSON shape in a test.

toJSON() returns a string, not an array. If you need the decoded structure, wrap with json_decode(..., true). Passing JSON_PRETTY_PRINT as the optional flag is handy for local debugging and for committing fixtures to version control.

Version pinning matters. PHP 8.1+ users should be on ^2.0; the 1.x line targets older PHP and its composer.json constraint may block a modern app. Check the installed tag with composer show strobotti/php-jwk if behavior does not match the README — the docs on GitHub reflect main, not necessarily your locked version.

Extension points are thin. There is no plugin system or config object. If you need custom serialization (e.g. adding an x5c certificate chain), you will be decoding toJSON(), mutating the array, and re-encoding — or subclassing the concrete JWK type and overriding toJSON(). The latter works because the base class is not final, but it is not an officially supported pattern, so pin your version if you rely on it.

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.
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
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