Skip to content

Key set (JWKSet)

You can create a JWKSet object using three static methods:

  • new JWKSet(array $keys): creates a JWKSet using a list of JWK objects.
  • JWKSet::createFromJson(string $json): creates a JWKSet using a JSON object.
  • JWKSet::createFromKeyData(array $values): creates a JWKSet using a decoded JSON object.

Below are all methods available for a JWKSet object. The variable $jwkset is a valid JWKSet object.

Warning

Please note a JWKSet object is an immutable object. When you add keys, you get a new JWKSet object.

<?php
// Returns all keys
$jwkset->all();

// Check if the key set has the key with the key ID 'KEY ID'.
$jwkset->has('KEY ID');

// Retrieve the key with the key ID 'KEY ID'.
$jwkset->get('KEY ID');

// Counts the keys in the key set.
$jwkset->count(); // The method count($jwkset) has the same behaviour.

// Adds a key to the key set.
// /!\ As the JWKSet object is immutable, this method will create a new key set. The previous key set is unchanged.
$newJwkset = $jwkset->with($jwk);

// Removes a key from the key set.
// /!\ As the JWKSet object is immutable, this method will create a new key set. The previous key set is unchanged.
$newJwkset = $jwkset->without('KEY ID');

// Selects a key according to the requirements.
// The first argument is the key usage ("sig" or "enc")
// The second argument is the algorithm to be used (optional)
// The third argument is an associative array of constraints (optional)
$key = $jwkset->selectKey('sig', $algorithm, ['kid' => 'KEY ID']);

// You can iterate on a key set
foreach($jwkset as $kid => $jwk) {
    // Action with the key done here
}

// The JWKSet object can be serialized into JSON
json_encode($jwkset);

Lookup by thumbprint URI

A key set can be searched with a JWK Thumbprint URI (RFC 9278). The thumbprint of every key is recomputed with the hash function named by the URI, so the lookup does not depend on the kid of the keys. Given the sub of a DPoP proof or of a SIOP id_token, it finds the key the token is bound to; null is returned when no key matches.

<?php
$key = $jwkset->selectKeyByThumbprintUri('urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs');

A malformed URI or an unsupported hash function throws; a key whose thumbprint cannot be computed is skipped, as selectKey() does with malformed keys.

Info

Available since 4.3.

Duplicate key IDs

RFC 7517 section 4.5 explicitly allows a key set to hold several keys sharing the same kid, typically an RSA key and an EC key that the application considers as equivalent alternatives.

Such keys are all kept:

<?php

use Jose\Component\Core\JWKSet;

// Both keys carry "kid" => "key-1"
$jwkset = new JWKSet([$rsaKey, $ecKey]);

count($jwkset);        // 2
$jwkset->get('key-1'); // The first key registered with that ID
  • get() and has() look the key ID up and return the first key carrying it.
  • without('key-1') removes every key carrying that ID.

Info

When several keys share a kid, prefer selectKey() over get(): it also takes the key usage and the algorithm into account, which is precisely what tells those keys apart.

Warning

Before 4.2, the keys were indexed by kid and the last key registered with a given ID silently replaced the previous one. See the migration guide.

Sealed helpers

Two methods carried an @internal annotation that did not match what they are:

  • sortKeys() was public only because the comparison was passed to usort() as a callable. It is a closure now, nothing in the library calls the method any more, and calling it is deprecated since 4.3.
  • getIterator() carried the same annotation although it is the IteratorAggregate contract, which wrongly told users not to iterate over a key set. Iterating is supported, as the example above shows.

Warning

JWKSet becomes final readonly in 5.0, and extending it raises a deprecation notice since 4.3. Like the key, it is a value object with no interface to decorate: move to composition now — build the key set with its constructor and keep your own state in the object that uses it.