Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code for each Exception #456

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
26 changes: 19 additions & 7 deletions src/CachedKeySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,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 JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
}
Expand All @@ -121,15 +124,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
);
}

/**
Expand All @@ -140,11 +149,11 @@ private function formatJwksForCache(string $jwks): array
$jwks = json_decode($jwks, true);

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::CACHED_KEY_MISSING);
}

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::CACHED_KEY_EMPTY);
}

$keys = [];
Expand Down Expand Up @@ -185,7 +194,7 @@ private function keyIdExists(string $keyId): bool
$jwksResponse->getReasonPhrase(),
$this->jwksUri,
),
$jwksResponse->getStatusCode()
ExceptionCodes::CACHED_KEY_GET_JWK
);
}
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
Expand Down Expand Up @@ -243,7 +252,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
69 changes: 69 additions & 0 deletions src/ExceptionCodes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?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.

{
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 SIGNATURE_VERIFICATION_FAILED = 9;
public const NBF_PRIOR_TO_DATE = 10;
public const IAT_PRIOR_TO_DATE = 11;
public const TOKEN_EXPIRED = 12;
public const SIGN_ALGORITHM_NOT_SUPPORTED = 13;
public const KEY_IS_NOT_STRING = 14;
public const OPENSSL_SIGNATURE = 15;
public const OPENSSL_CAN_NOT_SIGN_DATA = 16;
public const SODIUM_KEY_IS_NOT_STRING = 17;
public const SODIUM_FUNC_DOES_NOT_EXIST = 18;
public const SODIUM_KEY_LENGTH_ZERO = 19;
public const SODIUM_VERIFY_KEY_LENGTH_ZERO = 20;
public const SODIUM_VERIFY_SIGNATURE_EMPTY = 21;
public const SODIUM_EXCEPTION = 22;
public const SIGN_GENERAL_EXCEPTION = 23;
public const VERIFY_ALGORITHM_NOT_SUPPORTED = 24;
public const VERIFY_OPEN_SSL_ERROR = 25;
public const VERIFY_SODIUM_NOT_AVAILABLE = 26;
public const VERIFY_KEY_MATERIAL_IS_NOT_STRING = 27;
public const VERIFY_SODIUM_EXCEPTION = 28;
public const VERIFY_KEY_IS_NOT_STRING = 29;
public const DECODED_JSON_IS_NULL = 30;
public const ENCODED_JSON_IS_NULL = 31;
public const INVALID_JSON = 32;
public const KID_IS_EMPTY = 33;
public const KID_IS_INVALID = 34;
public const JSON_ERROR = 35;
public const KEY_ID_NOT_FOUND = 36;
public const OFFSET_SET_METHOD_NOT_IMPLEMENTED = 37;
public const OFFSET_UNSET_METHOD_NOT_IMPLEMENTED = 38;
public const JWKS_URI_IS_EMPTY = 39;
public const JWK_MISSING_KEYS = 40;
public const JWT_KEYS_IS_EMPTY = 41;
public const JWT_ALGORITHM_NOT_SUPPORTED = 42;
public const JWK_IS_EMPTY = 43;
public const JWT_MISSING_KTY_PARAMETER = 44;
public const JWT_MISSING_ALG_PARAMETER = 45;
public const JWT_RSA_KEYS_NOT_SUPPORTED = 46;
public const JWT_RSA_KEYS_MISSING_N_AND_E = 47;
public const JWT_OPEN_SSL_ERROR = 48;
public const JWK_EC_D_IS_NOT_SET = 49;
public const JWT_EC_CRV_IS_EMPTY = 50;
public const JWK_UNSUPPORTED_EC_CURVE = 51;
public const JWT_X_AND_Y_ARE_EMPTY = 52;
public const KEY_MATERIAL_IS_INVALID = 53;
public const KEY_MATERIAL_IS_EMPTY = 54;
public const KEY_ALGORITHM_IS_EMPTY = 55;
public const CACHED_KEY_MISSING = 56;
public const CACHED_KEY_EMPTY = 57;
public const CACHED_KEY_GET_JWK = 58;
public const JWK_OKP_MISSING = 59;
public const JWT_CRV_MISSING = 60;
public const JWT_CRV_UNSUPPORTED = 61;
public const JWT_X_MISSING = 62;
}
72 changes: 55 additions & 17 deletions src/JWK.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ public static function parseKeySet(array $jwks, ?string $defaultAlg = null): arr
$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 @@ -72,7 +78,11 @@ public static function parseKeySet(array $jwks, ?string $defaultAlg = null): arr
}

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 @@ -96,11 +106,17 @@ public static function parseKeySet(array $jwks, ?string $defaultAlg = null): arr
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 @@ -109,64 +125,86 @@ 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']);
return new Key($publicKey, $jwk['alg']);
case 'OKP':
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_OKP_MISSING);
}

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

if (empty(self::OKP_SUBTYPES[$jwk['crv']])) {
throw new DomainException('Unrecognised or unsupported OKP key subtype');
throw new DomainException('Unrecognised or unsupported OKP key subtype', ExceptionCodes::JWT_CRV_UNSUPPORTED);
}

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

// This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
Expand Down
Loading