PHP-JWT

repository·main·Indexed 27 days ago

https://github.com/googleapis/php-jwt

A lightweight PHP library for encoding and decoding JSON Web Tokens (JWT) compliant with RFC 7519. It supports various algorithms including HS256, RS256, PS256, and EdDSA. The library provides tools for handling JSON Web Key Sets (JWKS) via the JWK and CachedKeySet classes, as well as detailed exception handling for expired or invalid tokens.

Tokens
4.3K
Snippets
11
Records
20
Agent score
95%

What's inside php-jwt

  1. Use PS256 (RSASSA-PSS)

    main

    To use the PS256 algorithm, you must install phpseclib/phpseclib:^3.0 via Composer, as PHP's OpenSSL extension does not support RSASSA-PSS by default.

    composer install phpseclib/phpseclib:^3.0
    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    $jwt = JWT::encode($payload, $privateRsKey, 'PS256', 'keyid');
    $decoded = JWT::decode($jwt, new Key($publicKey, 'PS256'));
  2. Extract JWT Headers without verification

    main

    This library does not support decoding headers without verification because unverified headers can be tampered with. If you must access header values without validation, you must manually decode the first part of the JWT string using base64_decode and json_decode. Warning: This is vulnerable to attacks if the data is used for security decisions.

    use Firebase\JWT\JWT;
    
    $jwt = '...'; // Your JWT string
    
    // Manually decode the header part
    list($headersB64, $payloadB64, $sig) = explode('.', $jwt);
    $decoded = json_decode(base64_decode($headersB64), true);
    
    print_r($decoded);
  3. Install PHP-JWT via Composer

    main

    Use Composer to install the PHP-JWT library. If your PHP environment does not have libsodium installed, you should also install paragonie/sodium_compat to ensure support for algorithms like EdDSA.

    composer require firebase/php-jwt
    
    # Optional: install if libsodium is missing
    composer require paragonie/sodium_compat
  4. Decode JWTs using multiple keys

    main

    If your tokens use different Key IDs (kid), you can pass an associative array of Firebase\JWT\Key objects to JWT::decode(). The array keys should match the kid in the JWT header.

    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    $payload = ['iss' => 'example.org'];
    
    // Encode with specific Key IDs
    $jwt1 = JWT::encode($payload, $privateRsKey, 'RS256', 'kid1');
    $jwt2 = JWT::encode($payload, $privateEcKey, 'EdDSA', 'kid2');
    
    // Provide a map of keys for decoding
    $keys = [
        'kid1' => new Key($publicRsKey, 'RS256'),
        'kid2' => new Key($publicEcKey, 'EdDSA'),
    ];
    
    $decoded1 = JWT::decode($jwt1, $keys);
    $decoded2 = JWT::decode($jwt2, $keys);
  5. Encode and Decode with RS256 (RSA)

    main

    For RSA signatures, use JWT::encode() with a private key and JWT::decode() with a Key object containing the public key.

    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    $privateKey = '-----BEGIN RSA PRIVATE KEY----- ...';
    $publicKey = '-----BEGIN PUBLIC KEY----- ...';
    
    $payload = [
        'iss' => 'example.org',
        'aud' => 'example.com',
        'iat' => 1356999524,
        'nbf' => 1357000000
    ];
    
    $jwt = JWT::encode($payload, $privateKey, 'RS256');
    $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
    $decoded_array = (array) $decoded;
  6. Encode and Decode JWTs with HS256

    main

    Use JWT::encode() to create a token and JWT::decode() to verify and parse it. When decoding, you must provide a Firebase\JWT\Key object specifying the key and the algorithm. By default, JWT::decode() returns a stdClass object; cast it to (array) if you require an associative array. You can also set JWT::$leeway to account for clock skew between servers.

    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    $key = 'example_key_of_sufficient_length';
    $payload = [
        'iss' => 'example.org',
        'aud' => 'example.com',
        'iat' => 1356999524,
        'nbf' => 1357000000
    ];
    
    // Encode
    $jwt = JWT::encode($payload, $key, 'HS256');
    
    // Decode
    $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
    
    // To get an associative array:
    $decoded_array = (array) $decoded;
    
    // Account for clock skew (e.g., 60 seconds)
    JWT::$leeway = 60;
    $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
  7. Encode and Decode with EdDSA (Ed25519)

    main

    EdDSA requires libsodium. Keys are expected to be Base64 encoded. Use sodium_crypto_sign_keypair() to generate keys for testing.

    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    $keyPair = sodium_crypto_sign_keypair();
    $privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
    $publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
    
    $payload = [
        'iss' => 'example.org',
        'aud' => 'example.com',
        'iat' => 1356999524,
        'nbf' => 1357000000
    ];
    
    $jwt = JWT::encode($payload, $privateKey, 'EdDSA');
    $decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
    $decoded_array = (array) $decoded;
  8. Cast decoded JWT to an array

    main

    The JWT::decode method returns a stdClass object. If your application requires the decoded payload as an associative array, you can cast it using json_decode and json_encode.

    // return type is stdClass
    $decoded = JWT::decode($jwt, $keys);
    
    // cast to array
    $decoded = json_decode(json_encode($decoded), true);
  9. Decode using JWKs

    main

    You can use Firebase\JWT\JWK::parseKeySet($jwks) to convert a JSON Web Key Set (JWKS) into an associative array of Firebase\JWT\Key objects. This array can then be passed directly to JWT::decode().

    use Firebase\JWT\JWK;
    use Firebase\JWT\JWT;
    
    // $jwks is an associative array containing a 'keys' element
    $jwks = ['keys' => []];
    
    // Parse the JWKS and decode the token
    $decoded = JWT::decode($jwt, JWK::parseKeySet($jwks));
  10. Use CachedKeySet to fetch and cache JWKS

    main

    The CachedKeySet class fetches and caches JSON Web Key Sets (JWKS) from a public URI. It provides performance benefits through caching, automatically refreshes the cache if an unrecognized key is encountered (to accommodate key rotation), and includes an optional rate limiter that restricts lookups of invalid keys to 10 requests per second.

    To use CachedKeySet, you must provide:

    1. A JWKS URI.
    2. A PSR-7 compatible HTTP client.
    3. A PSR-17 compatible HTTP request factory.
    4. A PSR-6 compatible cache item pool.
    5. (Optional) An integer for $expiresAfter (seconds until JWKS expires).
    6. (Optional) A boolean for $rateLimit (set to true to enable the 10 RPS limit).
    use Firebase\\JWT\\CachedKeySet;
    use Firebase\\]JWT\\JWT;
    
    // The URI for the JWKS you wish to cache the results from
    $jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
    
    // Create an HTTP client (can be any PSR-7 compatible HTTP client)
    $httpClient = new GuzzleHttp\\Client();
    
    // Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
    $httpFactory = new GuzzleHttp\\Psr7\\HttpFactory();
    
    // Create a cache item pool (can be any PSR-6 compatible cache item pool)
    $cacheItemPool = Phpfastcache\\CacheManager::getInstance('files');
    
    $keySet = new CachedKeySet(
        $jwksUri,
        $httpClient,
        $httpFactory,
        $cacheItemPool,
        null, // $expiresAfter int seconds to set the JWKS to expire
        true  // $rateLimit    true to enable rate limit of 10 RPS on lookup of invalid keys
    );
    
    $jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above
    $decoded = JWT::decode($jwt, $keySet);
  11. Handle exceptions when decoding JWTs

    main

    When calling JWT::decode, several exceptions may be thrown depending on the nature of the error. All exceptions in the Firebase\JWT namespace extend UnexpectedValueException.

    Specific Exceptions

    • InvalidArgumentException: The provided key or key-array is empty or malformed.
    • DomainException: The provided algorithm is unsupported, the key is invalid, or an error occurred in OpenSSL/libsodium.
    • SignatureInvalidException: The JWT signature verification failed.
    • BeforeValidException: The JWT is being used before its nbf (not before) or iat (issued at) claims.
    • ExpiredException: The JWT is being used after its exp (expiration) claim.
    • UnexpectedValueException: The JWT is malformed, missing an algorithm, the algorithm does not match the key, or the key ID is invalid.

    Simplified Error Handling

    You can catch errors broadly by distinguishing between environmental/setup issues and JWT content issues:

    • LogicException: Errors related to environmental setup or malformed JWT Keys.
    • UnexpectedValueException: Errors related to the JWT signature and claims.
    use Firebase\JWT\JWT;
    use Firebase\JWT\SignatureInvalidException;
    use Firebase\JWT\BeforeValidException;
    use Firebase\JWT\ExpiredException;
    use DomainException;
    use InvalidArgumentException;
    use UnexpectedValueException;
    
    try {
        $decoded = JWT::decode($jwt, $keys);
    } catch (InvalidArgumentException $e) {
        // provided key/key-array is empty or malformed.
    } catch (DomainException $e) {
        // provided algorithm is unsupported OR
        // provided key is invalid OR
        // unknown error thrown in openSSL or libsodium OR
        // libsodium is required but not available.
    } catch (SignatureInvalidException $e) {
        // provided JWT signature verification failed.
    } catch (BeforeValidException $e) {
        // provided JWT is trying to be used before "nbf" claim OR
        // provided JWT is trying to be used before "iat" claim.
    } catch (ExpiredException $e) {
        // provided JWT is trying to be used after "exp" claim.
    } catch (UnexpectedValueException $e) {
        // provided JWT is malformed OR
        // provided JWT is missing an algorithm / using an unsupported algorithm OR
        // provided JWT algorithm does not match provided key OR
        // provided key ID in key/key-array is empty or invalid.
    }