arrow-left

All pages
gitbookPowered by GitBook
1 of 13

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Signed tokens and

Serialization

The RFC7515arrow-up-right (JWS) and RFC7516arrow-up-right (JWE) introduce several serialization modes.

  • Compact

  • JSON Flattened

  • JSON General

The Compact mode is most know and commonly used as it is compact and URL safe i.e. it is designed for web context. JSON Flattened and General are not URL safe, but provides features that may fit on your application context.

hashtag
JWS Serialization

To use the JWS serializers, you have to install the jwt-signature component.

hashtag
JWS Compact

This serialization mode is probably the one you know the most. It it a string composed of three parts encoded in Base64 Url Safe and separated by a dot (.).

The serializer class is Jose\Component\Signature\Serializer\CompactSerializer. The associated name is jws_compact.

Example:

There are some limitations when you use this serialization mode:

  • Unprotected header not supported.

  • Unencoded payload must contain characters within the following range of ASCII characters: 0x20-0x2d and 0x2f-0x7e

hashtag
JWS JSON Flattened

This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object.

The serializer class is Jose\Component\Signature\Serializer\JSONFlattenedSerializer. The associated name is jws_json_flattened.

Example:

hashtag
JWS JSON General

This serialization mode is similar to the JWS JSON Flattened, but may contain more than one signature. It it a JSON object.

The serializer class is Jose\Component\Signature\Serializer\JSONGeneralSerializer. The associated name is jws_json_general.

Example:

hashtag
JWS Serializer Manager

The serializer manager can be helpful when your application deals more than one serialization mode.

hashtag
JWE Serialization

To use the JWE serializers, you have to install the jwt-encryption component.

hashtag
JWE Compact

This serialization mode is probably the one you know the most. It it a string composed of five parts encoded in Base64 Url Safe and separated by a dot (.).

The serializer class is Jose\Component\Encryption\Serializer\CompactSerializer. The associated name is jwe_compact.

Example:

There are some limitations when you use this serialization mode:

  • No Additional Authentication Data can be used.

  • No shared unprotected header or per-recipient header can be used.

hashtag
JWE JSON Flattened

This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object.

The serializer class is Jose\Component\Encryption\Serializer\JSONFlattenedSerializer. The associated name is jwe_json_flattened.

Example:

hashtag
JWE JSON General

This serialization mode is similar to the JWE JSON Flattened, but may contain more than one recipient. It it a JSON object.

The serializer class is Jose\Component\Encryption\Serializer\JSONGeneralSerializer. The associated name is jwe_json_general.

Example:

hashtag
JWE Serializer Manager

The serializer manager can be helpful when your application deals more than one serialization mode.

composer require web-token/jwt-signature
eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn
{
  "payload": "SW4gb3VyIHZpbGxhZ2UsIGZvbGtzIHNheSBHb2QgY3J1bWJsZXMgdXAgdGhlIG9sZCBtb29uIGludG8gc3RhcnMu",
  "protected": "eyJhbGciOiJFUzI1NiJ9",
  "header": {
    "kid": "myEcKey"
  },
  "signature": "b7V2UpDPytr-kMnM_YjiQ3E0J2ucOI9LYA7mt57vccrK1rb84j9areqgQcJwOA00aWGoz4hf6sMTBfobdcJEGg"
}
{
  "payload": "SW4gb3VyIHZpbGxhZ2UsIGZvbGtzIHNheSBHb2QgY3J1bWJsZXMgdXAgdGhlIG9sZCBtb29uIGludG8gc3RhcnMu",
  "signatures": [
    {
      "protected": "eyJhbGciOiJSUzI1NiJ9",
      "header": {
        "kid": "myRsaKey"
      },
      "signature": "B04c24gSnpVm1Z-_bemfyNMCpZm6Knj1yB-yzaIOvijsWfDgoF_mSJccTIbzapNgwJudnobr5iDOfZWiRR9iqCyDJLe5M1S40vFF7MFEI3JecYRgrRc6n1lTkYLMRyVq48BwbQlmKgPqmK9drun3agklsr0FmgNx65pfmcnlYdXsgwxf8WbgppefrlrMImp-98-dNtBcUL8ce1aOjbcyVFjGMCzpm3JerQqIzWQvEwBstnMEQle73KHcyx_nsTmlzY70CaydbRTsciOATL7WfiMwuX1q9Y2NIpTg3CbOTWKdwjh7iyfiAKQxNBaF2mApnqj9hjpf8GwR-CfxAzJtPg"
    },
    {
      "protected": "eyJhbGciOiJFUzI1NiJ9",
      "header": {
        "kid": "myEcKey"
      },
      "signature": "2cbugKq0ERaQMh01n2B-86EZFYleeMf8bsccaQMxzOxAg14PxfjR3IImvodTJYqkmfBJYW203etz2-7ZtJUOGw"
    },
    {
      "protected": "eyJhbGciOiJIUzI1NiJ9",
      "header": {
        "kid": "myMacKey"
      },
      "signature": "e7R9gjx0RsUNa3c7qd8k9mQGEhtcG8vsN1W7jbLb2MA"
    }
  ]
}
<?php

require_once 'vendor/autoload.php';

use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Signature\Serializer;

$jsonConverter = new StandardConverter();

$manager = Serializer\JWSSerializerManager::create([
    new Serializer\CompactSerializer($jsonConverter),
    new Serializer\JSONFlattenedSerializer($jsonConverter),
    new Serializer\JSONGeneralSerializer($jsonConverter),
]);

// Serializes the second signature (index = 1) of the variable $jws (JWS object) into JSON Flattened serialization mode.
$token = $manager->serialize('jws_json_flattened', $jws, 1);

// Retrieve the JWS object from a token
$jws = $manager->unserialize($token);
eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw
{
  "protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
  "unprotected":{"jku":"https://server.example.com/keys.jwks"},
  "header":{"alg":"A128KW","kid":"7"},
  "encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",
  "iv":"AxY8DCtDaGlsbGljb3RoZQ",
  "ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
  "tag":"Mz-VPPyU4RlcuYv1IwIvzw"
}
 {
  "protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
  "unprotected":{"jku":"https://server.example.com/keys.jwks"},
  "recipients":[
    {
      "header":{"alg":"RSA1_5","kid":"2011-04-29"},
      "encrypted_key":"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"
    },
    {
      "header":{"alg":"A128KW","kid":"7"},
      "encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"
    }
  ],
  "iv":"AxY8DCtDaGlsbGljb3RoZQ",
  "ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
  "tag":"Mz-VPPyU4RlcuYv1IwIvzw"
 }
<?php

require_once 'vendor/autoload.php';

use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Encryption\Serializer;

$jsonConverter = new StandardConverter();

$manager = Serializer\JWESerializerManager::create([
    new Serializer\CompactSerializer($jsonConverter),
    new Serializer\JSONFlattenedSerializer($jsonConverter),
    new Serializer\JSONGeneralSerializer($jsonConverter),
]);

// Serializes the second recipient (index = 1) of the variable $jwe (JWE object) into JSON Flattened serialization mode.
$token = $manager->serialize('jwe_json_flattened', $jwe, 1);

// Retrieve the JWE object from a token
$jwe = $manager->unserialize($token);

Encrypted tokens and

Custom Algorithm

This framework provides dozens of signature or encryption algorithms. If your application uses a custom algorithm or if another algorithm has been recently approved in the JWT context, it may be interesting to use it with this framework.

Hopefully, this is very easy. In the following example, we will create a class to use the ChaCha20 + Poly 1305 (IETF variant) encryption algorithm as a Key Encryption Algorithm.

Additional Authentication Data (AAD)

The Additional Authenticated Data (AAD) is an input to an Authenticated Encryption operation. The AAD is integrity protected but not encrypted.

Its value can be any string you want that is needed by your application. With the example below, we will add a dummy AAD:

$jwe = $jweBuilder
    ->create()
    ->withPayload('...')
    ->withSharedProtectedHeader([
        'enc' => 'A256CBC-HS512',
        'alg' => 'RSA-OAEP-256',
        'zip' => 'DEF',
    ])
    ->addRecipient($recipient_key)
    ->withAAD('A,B,C,D')
    ->build();

Note: when the AAD is set, the Compact Serialization mode is not available.

Unprotected Header

You may want to set data in a token header that are not important for your application (e.g. general information). The integrity protection of the data is therefore not needed at all.

The RFC7515arrow-up-right introduces an unprotected header. This header is supported by this framework.

With the example below, we will create a signed token with some unprotected header parameters:

$jws = $jwsBuilder
    ->create()
    ->withPayload('...')
    ->addSignature($jwk, ['alg' => 'HS256'], ['description' => 'Small description here', 'author' => 'John Doe'])
    ->build();

The variable $jws will be a valid JWS object with one signature and both headers.

Note: when an unprotected header is set, the Compact Serialization mode is not available.

<?php

declare(strict_types=1);

namespace Acme\Algorithm;

use Base64Url\Base64Url;
use Jose\Component\Core\JWK;
use const Sodium\CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES;

/**
 * This algorithm is a custom algorithm that use the ChaCha20 + Poly 1305 with a 192 bits nonce (IETF variant).
 */
final class ChaCha20Poly1305IETF implements KeyEncryption //The algorithm acts as a Key Encryption algorithm
{
    /**
     * {@inheritdoc}
     */
    public function name(): string
    {
        return 'ChaCha20+Poly1305+IETF'; // The name of our algorithm. This name will be used in our JWE headers
    }

    /**
     * {@inheritdoc}
     */
    public function allowedKeyTypes(): array
    {
        return ['oct']; // Key types for this algorithm are octet keys
    }

    /**
     * {@inheritdoc}
     */
    public function encryptKey(JWK $key, string $cek, array $completeHeader, array &$additionalHeader): string
    {
        $this->checkKey($key); // We check the key
        $kek = Base64Url::decode($key->get('k')); // We retrieve the secret
        $nonce = random_bytes(CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES); // We create a nonce
        $additionalHeader['nonce'] = Base64Url::encode($nonce); // We add the nonce to the header

        return sodium_crypto_aead_chacha20poly1305_ietf_encrypt($cek, '', $nonce, $kek); // We return the encrypted CEK
    }

    /**
     * {@inheritdoc}
     */
    public function decryptKey(JWK $key, string $encrypted_cek, array $header): string
    {
        $this->checkKey($key); // We check the key
        $this->checkAdditionalParameters($header); // We verify the nonce is in the headers
        $nonce = Base64Url::decode($header['nonce']); // We retrieve the nonce
        $kek = Base64Url::decode($key->get('k')); // an the secret

        $decrypted = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($encrypted_cek, '', $nonce, $kek); // We try to decrypt the CEK
        if (false === $decrypted) { // If it fails we throw an exception
            throw new \RuntimeException('Unable to decrypt.');
        }

        return $decrypted; // Otherwise we return the decrypted CEK
    }

    /**
     * @param JWK $key
     */
    protected function checkKey(JWK $key)
    {
        if (!in_array($key->get('kty'), $this->allowedKeyTypes())) {
            throw new \InvalidArgumentException('Wrong key type.');
        }
        if (!$key->has('k')) {
            throw new \InvalidArgumentException('The key parameter "k" is missing.');
        }
    }

    /**
     * @param array $header
     */
    protected function checkAdditionalParameters(array $header)
    {
        foreach (['nonce'] as $k) {
            if (!array_key_exists($k, $header)) {
                throw new \InvalidArgumentException(sprintf('Parameter "%s" is missing.', $k));
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getKeyManagementMode(): string
    {
        return self::MODE_ENCRYPT; //Key Management Mode is 'enc'.
    }
}

Unencoded Payload

The RFC7797arrow-up-right allows the use of an unencoded payload for the signed tokens. This behaviour is interesting when your tokens have a detached payload and may reduce the token computation.

Please note that when the Compact Serialization mode is used, the characters of the payload must be limited to the following ASCII ranges:

  • From 0x20 to 0x2d

  • From 0x2f to 0x7e

This feature is built in the framework and is enabled when the b64 header parameter is set to false. As per the RFC, this header MUST be protected and also listed as a critical (crit) header parameter.

Example:

As a remainder, both b64 and crit parameters MUST be in the protected header.

Multiple Signatures

When you need to sign the same payload for several audiences, you may want to do it at once. The JWS Builder supports multiple signatures.

With the example below, we will create three signatures using three different algorithms (and signature keys):

The variable $jws will be a valid JWS object with all computed signatures. Next step is the serialization of these signatures.

$jws = $jwsBuilder
    ->create()
    ->withPayload('Hello World!')
    ->addSignature($jwk, ['alg' => 'HS256', 'b64' => false, 'crit' => ['b64']])
    ->build();
$jws = $jwsBuilder
    ->create()
    ->withPayload('...')
    ->addSignature($signature_key1, ['alg' => 'HS256'])
    ->addSignature($signature_key2, ['alg' => 'RS384'])
    ->addSignature($signature_key3, ['alg' => 'ES512'])
    ->build();
use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Signature\Serializer;

// The JSON Converter.
$jsonConverter = new StandardConverter();

$manager = Serializer\JWSSerializerManager::create([
    new Serializer\CompactSerializer($jsonConverter),
    new Serializer\JsonFlattenedSerializer($jsonConverter),
    new Serializer\JsonGeneralSerializer($jsonConverter),
]);

$tokenWithAllSignatures = $manager->serialize('jws_json_general', $jws);
$compactTokenWithSignatureAtIndex1 = $manager->serialize('jws_compact', $jws, 1);
$flattenedTokenWithSignatureAtIndex2 = $manager->serialize('jws_json_flattened', $jws, 2);

Unprotected Headers

As well as the signed tokens, the encrypted tokens also have unprotected header. But with one difference: there are two unprotected headers:

  • Shared unprotected header applicable to all recipients.

  • Per-recipient unprotected header.

With the example below, we will create an encrypted token for two recipient and some unprotected header parameters:

The variable $jwe will be a valid JWE object built for two recipients. The unprotected header parameter author is applicable to the whole token while message and description are available only for the first and second recipient respectively.

Note: when an unprotected header is set, the Compact Serialization mode is not available.

$jwe = $jweBuilder
    ->create()
    ->withPayload('...')
    ->withSharedProtectedHeader(['enc' => 'A256GCM', 'alg' => 'A256KW'])
    ->withSharedHeader(['author' => 'John Doe'])
    ->addRecipient($recipient_public_key_1, ['message' => 'Hello World!'])
    ->addRecipient($recipient_public_key_2, ['description' => 'Nice song for you'])
    ->build();

Detached Payload

As per the RFC7519arrow-up-right,the payload of a JWS may be detached. This framework supports this feature.

hashtag
JWS Creation

There is not much difference between the creation of a JWS with or without detached payload. The following example comes from the JWS Creation page. There is only one argument that will change during the call of withPayload.

And voilà! When you will serialize this token, the payload will not be present.

hashtag
JWS Loading

The loading of a signed token with a detached payload is as easy as when the payload is attached. The only difference is that you have to pass the payload to the JWS Verifier when you want to check the signature.

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;

// The algorithm manager with the HS256 algorithm.
$algorithmManager = AlgorithmManager::create([
    new HS256(),
]);

// Our key.
$jwk = JWK::create([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// The JSON Converter.
$jsonConverter = new StandardConverter();

// We instantiate our JWS Builder.
$jwsBuilder = new JWSBuilder(
    $jsonConverter,
    $algorithmManager
);

// The payload we want to sign
$payload = $jsonConverter->encode([
    'iat' => time(),
    'nbf' => time(),
    'exp' => time() + 3600,
    'iss' => 'My service',
    'aud' => 'Your application',
]);

$jws = $jwsBuilder
    ->create()                               // We want to create a new JWS
    ->withPayload($payload, true)            // /!\ Here is the change! We set the payload and we indicate it is detached
    ->addSignature($jwk, ['alg' => 'HS256']) // We add a signature with a simple protected header
    ->build();
<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Jose\Component\Signature\Serializer\CompactSerializer;

// The algorithm manager with the HS256 algorithm.
$algorithmManager = AlgorithmManager::create([
    new HS256(),
]);

// Our key.
$jwk = JWK::create([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// The JSON Converter.
$jsonConverter = new StandardConverter();

// The serializer manager. We only use the JWS Compact Serialization Mode.
$serializerManager = JWSSerializerManager::create([
    new CompactSerializer($jsonConverter),
]);

// We instantiate our JWS Verifier.
$jwsVerifier = new JWSVerifier(
    $algorithmManager
);

// The detached payload
$payload = '{"iat":1507896992,"nbf":1507896992,"exp":1507900592,"iss":"My service","aud":"Your application"}';

// The input we want to check
$token = 'eyJhbGciOiJIUzI1NiJ9..eycp9PTdgO4WA-68-AMoHPwsKDr68NhjIQKz4lUkiI0';

// We try to load the token.
$jws = $serializerManager->unserialize($token);

// We verify the signature.
// /!\ The third argument is the detached payload.
$jwsVerifier->verifyWithKey($jws, $jwk, $payload);

Multiple Recipients

When you need to encrypt the same payload for several audiences, you may want to do it at once. The JWE Builder supports multiple recipients.

With the example below, we will create an encrypted token for three different recipients using three different key encryption algorithms.

Important notes:

  • The content encryption algorithm MUST be the same for all recipients.

  • The Key Management Modes of the key encryption algorithms MUST be compatible (see table below).

Note: when an unprotected header is set, the Compact Serialization mode is not available.

hashtag
Key Management Modes

Each Key Encryption Algorithm has its own Key Management Mode.

hashtag
Key Encryption Algorithms And Associated Key Management Mode.

hashtag
Compatibility table between Key Management Modes:

A192KW

X

A256KW

X

ECDH-ES

X

ECDH-ES+A128KW

X

ECDH-ES+A192KW

X

ECDH-ES+A256KW

X

PBES2-HS256+A128KW

X

PBES2-HS384+A192KW

X

PBES2-HS512+A256KW

X

RSA1_5

X

RSA-OAEP

X

RSA-OAEP-256

X

A128GCMKW

X

A192GCMKW

X

A256GCMKW

X

NO

YES

NO

Direct Key Agreement

NO

NO

NO

NO

NO

Key Agreement with Key Wrapping

YES

YES

NO

NO

NO

Direct Encryption

NO

NO

NO

NO

NO

Algorithm Key Management Mode

Key Encryption

Key Wrapping

Direct Key Agreement

Key Agreement with Key Wrapping

Direct Encryption

dir

X

A128KW

Key Management Mode

Key Encryption

Key Wrapping

Direct Key Agreement

Key Agreement with Key Wrapping

Direct Encryption

Key Encryption

YES

YES

NO

YES

NO

Key Wrapping

YES

X

YES

$jweBuilder
    ->create()
    ->withPayload('...')
    ->withSharedProtectedHeader(['enc' => 'A128GCM'])
    ->addRecipient($recipient_key_1, ['alg' => 'RSA1_5'])
    ->addRecipient($recipient_key_2, ['alg' => 'RSA-OAEP-256'])
    ->build();

Nested Tokens

JWT can be signed or encrypted and both. A nested token is a signed token enclosed in an encrypted one. This order is very important: signed then encrypted.

The NestedTokenLoader and NestedTokenBuilder classes will help you to create nested tokens with ease. They are provided by the web-token/jwt-encryption component. However, you must also install the following component to use it:

  • web-token/jwt-checker

  • web-token/jwt-signature

hashtag
Nested Token Loading

To instantiate the NestedTokenLoader, you need a JWSLoader and a JWELoader.

Its use is very straightforward, you just have to call the method load using the token, the encryption and signature key sets.

The last argument ($signature in the following example) will represents the signature index of the verified signature. This is only useful when multiple signature support is used.

hashtag
Nested Token Building

To instantiate the NestedTokenBuilderder, you will need the following components:

  • a JWSBuilder,

  • a JWEBuilder,

  • a JWESerializerManager

Its use is a bit more complicated than the loading as the nested token may be designed for several recipients or may have several signatures.

As a reminder, if one of the following parameter is set, the compact serialization mode cannot be used:

  • signature unprotected header,

  • JWE shared unprotected header,

  • recipient unprotected header,

hashtag
Symfony Bundle

hashtag
Configuration

Hereafter an example of a Symfony application configuration:

This configuration will create two public services:

  • jose.nested_token_loader.loader_1

  • jose.nested_token_builder.builder_1

These services can be called from the container (unless private) or injected in your services.

hashtag
Configuration Helper

As any other services, you can create a nested token loader or builder from another bundle extension. The following bundle extension class will create the same configuration and services as above.

,
  • a JWSSerializerManager

  • Additional Authenticated Data.
    use Jose\Component\Encryption\NestedTokenLoader;
    
    $nestedTokenLoader = new NestedTokenLoader($jweLoader, $jwsLoader);
    $jws = $nestedTokenLoader->load($token, $encryptionKeySet, $signatureKeySet, $signature);
    use Jose\Component\Encryption\NestedTokenBuilder;
    
    $nestedTokenBuilder = new NestedTokenBuilder($jweLoader, $jweSerializerManager, $jwsLoader, $jwsSerializerManager);
    $token = $builder->create(
        $payload,                                     // The payload to protect
        [[                                            // A list of signatures. 'key' is mandatory and at least one of 'protected_header'/'header' has to be set.
            'key'              => $signature_key,     // The key used to sign. Mandatory.
            'protected_header' => ['alg' => 'PS256'], // The protected header. Optional.
            'header'           => ['foo' => 'bar'],   // The unprotected header. Optional.
        ]],
        'jws_json_flattened',                         // The serialization mode for the JWS
        ['alg' => 'RSA-OAEP', 'enc' => 'A128GCM'],    // The shared protected header. Optional.
        ['foo' => 'bar'],                             // The shared unprotected header. Optional.
        [[                                            // A list of recipients. 'key' is mandatory.
            'key'    => $encryption_key,              // The recipient key.
            'header' => ['bar' => 'foo'],             // The recipient unprotected header.
        ]],
        'jwe_json_flattened'                          // The serialization mode for the JWE.
        '1, 2, 3, 4'                                  // Additional Authenticated Data (AAD). Optional.
    );
    jose:
        nested_token:
            loaders:
                loader_1:
                    signature_algorithms: ['PS256']
                    key_encryption_algorithms: ['RSA-OAEP']
                    content_encryption_algorithms: ['A128GCM']
                    jws_serializers: ['jws_compact']
                    jws_header_checkers: [...]
                    jwe_serializers: ['jwe_compact']
                    jwe_header_checkers: [...]
                    is_public: true
            builders:
                builder_1:
                    signature_algorithms: ['PS256']
                    key_encryption_algorithms: ['RSA-OAEP']
                    content_encryption_algorithms: ['A128GCM']
                    jws_serializers: ['jws_compact']
                    jwe_serializers: ['jwe_compact']
                    is_public: true
    class AcmeExtension extends Extension implements PrependExtensionInterface
    {
        ...
    
        public function prepend(ContainerBuilder $container)
        {
            ConfigurationHelper::addNestedTokenLoader($container, 'loader_1', ['jwe_compact'], ['RSA-OAEP'], ['A128GCM'], ['DEF'], [], ['jws_compact'], ['PS256'], [], true, []);
            ConfigurationHelper::addNestedTokenBuilder($container, 'builder_1', ['jwe_compact'], ['RSA-OAEP'], ['A128GCM'], ['DEF'], ['jws_compact'], ['PS256'], true, []);
        }
    }

    Advanced Topics

    • Serialization

    • Nested Tokens

    • Custom Algorithm

    • Signed tokens and

    • Encrypted tokens and

  • Unprotected Header
    Multiple Signatures
    Detached Payload
    Unprotected Header
    Multiple Recipients
    Unencoded Payload
    Additional Authentication Data (AAD)