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

gree/jose

gree/jose is a PHP JOSE library for securely signing and encrypting data with JWS and JWE. It supports JWK key management and JWT workflows, making it useful for token-based authentication and interoperable JSON security in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

gree/jose is a standalone PHP JOSE toolkit. It ships as a set of low-level components (algorithms, builders, verifiers, serializers, key factories) rather than a single "sign this JWT" facade. Plan for roughly five minutes of wiring before your first token.

1. Install and pin.

composer require gree/jose

Because the package is archived, pin to a known-good tag in composer.json ("gree/jose": "4.x" etc.) and record the exact version. There will be no security patches — treat this as a frozen dependency and audit it as part of your supply chain.

2. Read these classes first. Everything else branches off them:

  • Jose\Component\Core\AlgorithmManager — whitelist of algorithms you will accept.
  • Jose\Component\KeyManagement\JWKFactory — creates/loads keys.
  • Jose\Component\Signature\JWSBuilder + JWSVerifier — sign/verify JWTs.
  • Jose\Component\Signature\Serializer\CompactSerializer — the xxx.yyy.zzz string form.
  • Jose\Component\Checker\ClaimCheckerManager — exp/nbf/iat validation (separate from signature verification).

3. First working JWT. The minimal happy path with a symmetric secret:

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

$algorithms = AlgorithmManager::create([new HS256()]);
$builder    = new JWSBuilder($algorithms);
$serializer = new CompactSerializer();
$verifier   = new JWSVerifier($algorithms);

$key = JWKFactory::createFromSecret('a-32-byte-or-longer-secret', ['alg' => 'HS256', 'use' => 'sig']);

$jws = $builder->create()
    ->withPayload(json_encode(['sub' => '42', 'iat' => time(), 'exp' => time() + 3600]))
    ->addSignature($key, ['alg' => 'HS256', 'typ' => 'JWT'])
    ->build();

$token = $serializer->serialize($jws, 0);

// later
$jws     = $serializer->unserialize($token);
$isValid = $verifier->verifyWithKey($jws, $key, 0);   // bool — NOT "is the JWT valid"

4. First real use case. Issue a bearer JWT in a login endpoint, verify it in middleware. Wire a service provider that registers AlgorithmManager, JWSBuilder, JWSVerifier, CompactSerializer, ClaimCheckerManager as singletons — do not construct them per request.

5. Then extend to asymmetric. Swap HS256 for RS256 and load the key from a PEM:

$privateKey = JWKFactory::createFromKeyFile('/etc/keys/jwt.pem', null, ['alg' => 'RS256', 'use' => 'sig']);
$publicJwk  = $privateKey->toPublic();

Implementation Patterns

Algorithm whitelisting per flow. Never reuse a single AlgorithmManager for both signing and verifying, and never accept none or every algorithm in the library. Expose the lists via config so the security team can audit them:

// config/jose.php
return [
    'signing_algorithms'   => ['RS256'],
    'encryption_algorithms'=> ['RSA-OAEP-256'],
    'content_algorithms'   => ['A256GCM'],
];

Container-registered builder/verifier pairs. In Laravel bind one builder per algorithm set. A typical provider:

$this->app->singleton(JWSBuilder::class, fn () =>
    new JWSBuilder(AlgorithmManager::create([new RS256()]))
);
$this->app->singleton(JWSVerifier::class, fn () =>
    new JWSVerifier(AlgorithmManager::create([new RS256()]))
);

Issue + verify as two services. Keep token minting (TokenIssuer) and validation (TokenValidator) separate. The validator should do three distinct steps, and developers routinely forget the last two:

$jws = $serializer->unserialize($token);

// 1. signature
if (! $verifier->verifyWithKey($jws, $publicJwk, 0)) {
    throw new InvalidTokenException('bad signature');
}

// 2. header checks (alg, typ, kid, crit)
$headerChecker->check($jws->getSignature(0)->getProtectedHeaders(), ['alg', 'typ']);

// 3. claim checks (exp, nbf, iat, iss, aud)
$claims = json_decode($jws->getPayload(), true);
$claimCheckerManager->check($claims, ['exp', 'nbf', 'iss', 'aud']);

JWK Set for rotation. Represent your signing keys as a JWKSet loaded from disk or a secrets manager. Verify with verifyWithKeySet so a kid header selects the correct key. Store kid explicitly in the public key metadata; don't compute it from the thumbprint unless you control both sides. Expose the public set at /.well-known/jwks.json via:

header('Content-Type: application/json');
echo json_encode((new JWKSet(array_map(fn ($k) => $k->toPublic(), $publicKeys)))->jsonSerialize());

JWE for sensitive payloads. If the JWT carries anything more sensitive than a subject id, encrypt it. Composition is JWS-then-JWE (sign -> encrypt, decrypt -> verify) so the recipient knows who signed only after decrypting:

$jwe = (new JWEBuilder(
    AlgorithmManager::create([new RSAOAEP256()]),
    AlgorithmManager::create([new A256GCM()]),
    new CompressionMethodManager([])
))->create()
  ->withPayload($serializedJws)
  ->addRecipient($recipientJwk)
  ->build();

$compactJwe = (new \Jose\Component\Encryption\Serializer\CompactSerializer())->serialize($jwe, 0);

Token guard / middleware pattern. Wrap the validator in a middleware so controllers get a typed principal. Return 401 for signature/parse failures and 403 for claim failures — clients and ops teams distinguish them. Cache verified public keys in Redis keyed by kid to avoid PEM parsing on every request.

Testing patterns. Use a fixed symmetric key in test config and a frozen clock by injecting time() (or use the ExpirationTimeChecker skew arg). Assert negative cases: expired, wrong aud, wrong key, tampered payload, alg: none attempt, header kid that doesn't exist.

Interop. Use the JWK thumbprint ($jwk->thumbprint('sha256')) when matching against another system's key registry. Export public keys as PEM/JWK for other services; keep private keys only in the signing service.

Gotchas and Tips

Archived status is the headline gotcha. No CVE fixes. Do a one-time source read of Jose/Component/*, vendor the lockfile, and consider firebase/php-jwt or web-token/jwt-framework (the successor lineage) if you need an actively maintained path. Many of gree/jose's APIs moved to web-token, so a migration is mechanical.

verifyWithKey() returning true does not mean the token is valid. It only checks the signature over the payload. exp, nbf, iat, iss, and aud are not checked for you. You must build a ClaimCheckerManager with the checkers you want — and pass the claim names to check() so an unchecked claim can't silently pass because you forgot one. Same for headers via HeaderCheckerManager.

No aud/iss in the defaults. AudienceChecker and IssuerChecker take the expected value in their constructor; wire them per tenant/consumer. Getting aud wrong is the most common JWT security ticket.

Clock skew. ExpirationTimeChecker and NotBeforeChecker accept an allowed skew in seconds; the default of 0 triggers flaky failures across hosts with unsynced clocks. Set ~30–60 seconds.

Key alg/use mismatches throw. JWKFactory::createFromSecret($secret, ['alg' => 'HS256', 'use' => 'sig']) — if you later call addSignature($key, ['alg' => 'RS256']) you get an exception from the algorithm manager, not a clear "wrong algorithm" message. Log the JWK's declared alg/use on failure to make this debuggable.

HS256 secrets are raw bytes. createFromSecret does not base64-decode. If your config stores a base64 secret, base64_decode before passing it in, or signatures will differ from other libraries. Use ≥32 bytes (256 bits).

Serializers are not interchangeable. CompactSerializer cannot represent multiple signatures or unprotected headers; JSONFlattenedSerializer / JSONGeneralSerializer can. unserialize on the wrong serializer throws InvalidArgumentException-shaped errors — pick once and enforce on your ingress (e.g., only accept compact JWTs on the wire).

addSignature requires a kid in headers if you plan to rotate. The library will not inject it. Set ['kid' => $jwk->get('kid')] or derive it via $jwk->thumbprint('sha256') and be consistent on the verifier side.

OpenSSL is a hard dependency for RS/ES/PS*.** Missing or misconfigured OpenSSL produces RuntimeExceptions from AlgorithmManager, not clean domain exceptions. Validate key loading at boot in production — a broken PEM should fail the deploy, not the first request.

ECDSA signature encoding. gree/jose emits JOSE-format (r||s) signatures; some non-PHP tools expect DER. If you're bridging to OpenSSL CLI or older Java, expect to convert. Similarly, ES256 requires the exact P-256 curve — don't pass prime256v1 strings where a curve name is expected unless the factory accepts it.

EdDSA / Ed25519 is not supported. No EdDSA in this library; plan on RS256/ES256 or the newer web-token if you need modern curves.

Exceptions are broad and untyped across components. Catch \Jose\Exception\* plus \InvalidArgumentException and \Throwable around parse/verify, and downgrade to a single InvalidTokenException at the boundary so you don't leak algorithm names or key ids to clients. Log the original for debugging.

Thumbprint and JWK Set helpers are handy but easy to misuse. JWKSet iteration preserves insertion order; the kid in the header is what selects the key, so make sure your kid values are unique. thumbprint('sha256') returns base64url — trim padding before comparing with external systems.

Performance. Building AlgorithmManager and JWSBuilder per request is measurable. Bind them as singletons. PEM parsing via JWKFactory::createFromKeyFile is also non-trivial — cache parsed JWK objects (or better, the derived public key) in Redis/APCu between requests.

Debugging tokens. Never dd($token) in production logs. Split the compact token and base64url-decode the header at least, to confirm alg, typ, kid, and crit. A missing typ: JWT breaks many strict verifiers even when gree/jose is happy.

Extension points. If you need custom checks, implement Jose\Component\Checker\ClaimChecker (single claim) or HeaderChecker; register via ClaimCheckerManager::create([...]). For custom algorithms, implement Jose\Component\Core\Algorithm and its signature/content sub-interfaces, then add to the manager whitelist. This is also how you'd shim Ed25519 if forced to.

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.
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
jjsquady/round-robin