Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/BeforeValidException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@

class BeforeValidException extends \UnexpectedValueException
{
public const NBF_PRIOR_TO_DATE = 1;
public const IAT_PRIOR_TO_DATE = 2;
}
20 changes: 16 additions & 4 deletions src/CachedKeySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ public function __construct(
public function offsetGet($keyId): Key
{
if (!$this->keyIdExists($keyId)) {
throw new OutOfBoundsException('Key ID not found');
throw new OutOfBoundsException(
'Key ID not found',
ExceptionCodes::KEY_ID_NOT_FOUND
);
}
return $this->keySet[$keyId];
}
Expand All @@ -119,15 +122,21 @@ public function offsetExists($keyId): bool
*/
public function offsetSet($offset, $value): void
{
throw new LogicException('Method not implemented');
throw new LogicException(
'Method not implemented',
ExceptionCodes::OFFSET_SET_METHOD_NOT_IMPLEMENTED
);
}

/**
* @param string $offset
*/
public function offsetUnset($offset): void
{
throw new LogicException('Method not implemented');
throw new LogicException(
'Method not implemented',
ExceptionCodes::OFFSET_UNSET_METHOD_NOT_IMPLEMENTED
);
}

private function keyIdExists(string $keyId): bool
Expand Down Expand Up @@ -198,7 +207,10 @@ private function getCacheItem(): CacheItemInterface
private function setCacheKeys(): void
{
if (empty($this->jwksUri)) {
throw new RuntimeException('JWKS URI is empty');
throw new RuntimeException(
'JWKS URI is empty',
ExceptionCodes::JWKS_URI_IS_EMPTY
);
}

// ensure we do not have illegal characters
Expand Down
57 changes: 57 additions & 0 deletions src/ExceptionCodes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Firebase\JWT;

class ExceptionCodes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest making this an interface (such as JwtException) and then having the Exceptions here all implement that interface.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bshaffer I made the changes you suggested. Please have another look.

{
public const KEY_NOT_EMPTY = 1;
public const WRONG_NUMBER_OF_SEGMENTS = 2;
public const INVALID_HEADER_ENCODING = 3;
public const INVALID_CLAIMS_ENCODING = 4;
public const PAYLOAD_NOT_JSON = 5;
public const EMPTY_ALGORITHM = 6;
public const DECODE_ALGORITHM_NOT_SUPPORTED = 7;
public const INCORRECT_KEY_FOR_ALGORITHM = 8;
public const SIGN_ALGORITHM_NOT_SUPPORTED = 9;
public const KEY_IS_NOT_STRING = 10;
public const OPENSSL_CAN_NOT_SIGN_DATA = 11;
public const SODIUM_KEY_IS_NOT_STRING = 12;
public const SODIUM_EXCEPTION = 13;
public const SIGN_GENERAL_EXCEPTION = 14;
public const VERIFY_ALGORITHM_NOT_SUPPORTED = 15;
public const VERIFY_OPEN_SSL_ERROR = 16;
public const VERIFY_SODIUM_NOT_AVAILABLE = 17;
public const VERIFY_KEY_MATERIAL_IS_NOT_STRING = 18;
public const VERIFY_SODIUM_EXCEPTION = 19;
public const VERIFY_KEY_IS_NOT_STRING = 20;
public const DECODED_JSON_IS_NULL = 21;
public const ENCODED_JSON_IS_NULL = 22;
public const INVALID_JSON = 23;
public const KID_IS_EMPTY = 24;
public const KID_IS_INVALID = 25;
public const JSON_ERROR = 26;

public const KEY_ID_NOT_FOUND = 27;
public const OFFSET_SET_METHOD_NOT_IMPLEMENTED = 28;
public const OFFSET_UNSET_METHOD_NOT_IMPLEMENTED = 29;

public const JWKS_URI_IS_EMPTY = 30;

public const JWK_MISSING_KEYS = 31;
public const JWT_KEYS_IS_EMPTY = 32;
public const JWT_ALGORITHM_NOT_SUPPORTED = 33;
public const JWK_IS_EMPTY = 34;
public const JWT_MISSING_KTY_PARAMETER = 35;
public const JWT_MISSING_ALG_PARAMETER = 36;
public const JWT_RSA_KEYS_NOT_SUPPORTED = 37;
public const JWT_RSA_KEYS_MISSING_N_AND_E = 38;
public const JWT_OPEN_SSL_ERROR = 39;
public const JWK_EC_D_IS_NOT_SET = 40;
public const JWT_EC_CRV_IS_EMPTY = 41;
public const JWK_UNSUPPORTED_EC_CURVE = 42;
public const JWT_X_AND_Y_ARE_EMPTY = 43;

public const KEY_MATERIAL_IS_INVALID = 44;
public const KEY_MATERIAL_IS_EMPTY = 45;
public const KEY_ALGORITHM_IS_EMPTY = 46;
}
1 change: 1 addition & 0 deletions src/ExpiredException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

class ExpiredException extends \UnexpectedValueException
{
public const TOKEN_EXPIRED = 1;
}
64 changes: 51 additions & 13 deletions src/JWK.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,17 @@ public static function parseKeySet(array $jwks, string $defaultAlg = null): arra
$keys = [];

if (!isset($jwks['keys'])) {
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
throw new UnexpectedValueException(
'"keys" member must exist in the JWK Set',
ExceptionCodes::JWK_MISSING_KEYS
);
}

if (empty($jwks['keys'])) {
throw new InvalidArgumentException('JWK Set did not contain any keys');
throw new InvalidArgumentException(
'JWK Set did not contain any keys',
ExceptionCodes::JWT_KEYS_IS_EMPTY
);
}

foreach ($jwks['keys'] as $k => $v) {
Expand All @@ -65,7 +71,11 @@ public static function parseKeySet(array $jwks, string $defaultAlg = null): arra
}

if (0 === \count($keys)) {
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
throw new UnexpectedValueException(
'No supported algorithms found in JWK Set',
ExceptionCodes::JWT_ALGORITHM_NOT_SUPPORTED

);
}

return $keys;
Expand All @@ -89,11 +99,17 @@ public static function parseKeySet(array $jwks, string $defaultAlg = null): arra
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
{
if (empty($jwk)) {
throw new InvalidArgumentException('JWK must not be empty');
throw new InvalidArgumentException(
'JWK must not be empty',
ExceptionCodes::JWK_IS_EMPTY
);
}

if (!isset($jwk['kty'])) {
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
throw new UnexpectedValueException(
'JWK must contain a "kty" parameter',
ExceptionCodes::JWT_MISSING_KTY_PARAMETER
);
}

if (!isset($jwk['alg'])) {
Expand All @@ -102,44 +118,66 @@ public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
// for parsing in this library. Use the $defaultAlg parameter when parsing the
// key set in order to prevent this error.
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
throw new UnexpectedValueException(
'JWK must contain an "alg" parameter',
ExceptionCodes::JWT_MISSING_ALG_PARAMETER
);
}
$jwk['alg'] = $defaultAlg;
}

switch ($jwk['kty']) {
case 'RSA':
if (!empty($jwk['d'])) {
throw new UnexpectedValueException('RSA private keys are not supported');
throw new UnexpectedValueException(
'RSA private keys are not supported',
ExceptionCodes::JWT_RSA_KEYS_NOT_SUPPORTED
);
}
if (!isset($jwk['n']) || !isset($jwk['e'])) {
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
throw new UnexpectedValueException(
'RSA keys must contain values for both "n" and "e"',
ExceptionCodes::JWT_RSA_KEYS_MISSING_N_AND_E
);
}

$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
$publicKey = \openssl_pkey_get_public($pem);
if (false === $publicKey) {
throw new DomainException(
'OpenSSL error: ' . \openssl_error_string()
'OpenSSL error: ' . \openssl_error_string(),
ExceptionCodes::JWT_OPEN_SSL_ERROR
);
}
return new Key($publicKey, $jwk['alg']);
case 'EC':
if (isset($jwk['d'])) {
// The key is actually a private key
throw new UnexpectedValueException('Key data must be for a public key');
throw new UnexpectedValueException(
'Key data must be for a public key',
ExceptionCodes::JWK_EC_D_IS_NOT_SET
);
}

if (empty($jwk['crv'])) {
throw new UnexpectedValueException('crv not set');
throw new UnexpectedValueException(
'crv not set',
ExceptionCodes::JWT_EC_CRV_IS_EMPTY
);
}

if (!isset(self::EC_CURVES[$jwk['crv']])) {
throw new DomainException('Unrecognised or unsupported EC curve');
throw new DomainException(
'Unrecognised or unsupported EC curve',
ExceptionCodes::JWK_UNSUPPORTED_EC_CURVE
);
}

if (empty($jwk['x']) || empty($jwk['y'])) {
throw new UnexpectedValueException('x and y not set');
throw new UnexpectedValueException(
'x and y not set',
ExceptionCodes::JWT_X_AND_Y_ARE_EMPTY
);
}

$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
Expand Down
Loading