From 49bd0306a4ce250d76f94ca36f05db2cd6ee83d5 Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Mon, 17 Jul 2023 22:44:22 -0500 Subject: [PATCH 1/6] feat(auth): Support Organization Name with Authorization --- src/Configuration/SdkConfiguration.php | 8 +- src/Exception/InvalidTokenException.php | 44 +++++----- src/Token.php | 22 +++++ src/Token/Validator.php | 102 +++++++++++++++++------- tests/Unit/Auth0Test.php | 48 +++++++++-- tests/Unit/Token/ValidatorTest.php | 41 +++++++++- tests/Unit/TokenTest.php | 4 +- 7 files changed, 201 insertions(+), 68 deletions(-) diff --git a/src/Configuration/SdkConfiguration.php b/src/Configuration/SdkConfiguration.php index b042814b..11798ef1 100644 --- a/src/Configuration/SdkConfiguration.php +++ b/src/Configuration/SdkConfiguration.php @@ -70,7 +70,7 @@ final class SdkConfiguration implements ConfigurableContract * @param null|string $redirectUri authentication callback URI, as defined in your Auth0 Application settings * @param null|string $clientSecret client Secret, found in the Auth0 Application settings * @param null|array $audience One or more API identifiers, found in your Auth0 API settings. The SDK uses the first value for building links. If provided, at least one of these values must match the 'aud' claim to validate an ID Token successfully. - * @param null|array $organization One or more Organization IDs, found in your Auth0 Organization settings. The SDK uses the first value for building links. If provided, at least one of these values must match the 'org_id' claim to validate an ID Token successfully. + * @param null|array $organization Allowlist containing one or more organization IDs/names. Reference your Auth0 organization settings for these values. By default, the SDK will use the first value provided when generating authorization links. * @param bool $usePkce Defaults to true. Use PKCE (Proof Key of Code Exchange) with Authorization Code Flow requests. See https://auth0.com/docs/flows/call-your-api-using-the-authorization-code-flow-with-pkce * @param null|array $scope One or more scopes to request for Tokens. See https://auth0.com/docs/scopes * @param string $responseMode Defaults to 'query.' Where to extract request parameters from, either 'query' for GET or 'form_post' for POST requests. @@ -425,7 +425,7 @@ public function getManagementTokenCache(?Throwable $exceptionIfNull = null): ?Ca /** * @param ?Throwable $exceptionIfNull * - * @return null|array the allowlist of Organization IDs + * @return null|array The configured allowlist of organization IDs/names. */ public function getOrganization(?Throwable $exceptionIfNull = null): ?array { @@ -808,7 +808,7 @@ public function pushAudience(array | string $audiences): ?array } /** - * @param array|string $organizations a string or array of strings representing Organization IDs to add to the allowlist + * @param array|string $organizations A string or array of strings representing organization IDs/names to add to the organization allowlist. * * @return null|array */ @@ -1097,7 +1097,7 @@ public function setManagementTokenCache(?CacheItemPoolInterface $managementToken } /** - * @param null|array $organization an allowlist of Organization IDs + * @param null|array $organization An allowlist of organizations IDs/names. */ public function setOrganization(?array $organization = null): self { diff --git a/src/Exception/InvalidTokenException.php b/src/Exception/InvalidTokenException.php index b68c821d..aad826ab 100644 --- a/src/Exception/InvalidTokenException.php +++ b/src/Exception/InvalidTokenException.php @@ -62,11 +62,6 @@ final class InvalidTokenException extends Exception implements Auth0Exception */ public const MSG_MISMATCHED_NONCE_CLAIM = 'Nonce (nonce) claim mismatch in the token; expected "%s", found "%s"'; - /** - * @var string - */ - public const MSG_MISMATCHED_ORG_ID_CLAIM = 'Organization Id (org_id) claim value mismatch in the token; expected "%s", found "%s"'; - /** * @var string */ @@ -112,11 +107,6 @@ final class InvalidTokenException extends Exception implements Auth0Exception */ public const MSG_MISSING_NONCE_CLAIM = 'Nonce (nonce) claim must be a string present in the token'; - /** - * @var string - */ - public const MSG_MISSING_ORG_ID_CLAIM = 'Organization Id (org_id) claim must be a string present in the token'; - /** * @var string */ @@ -142,6 +132,26 @@ final class InvalidTokenException extends Exception implements Auth0Exception */ public const MSG_UNSUPPORTED_SIGNING_ALGORITHM = 'Signature algorithm of "%s" is not supported. Expected the token to be signed with "RS256" or "HS256"'; + /** + * @var string + */ + public const MSG_ORGANIZATION_CLAIM_BAD = 'Token organization claim (`org_id` or `org_name`) must be a string'; + + /** + * @var string + */ + public const MSG_ORGANIZATION_CLAIM_MISSING = 'Token organization claim (`org_id` or `org_name`) was not found'; + + /** + * @var string + */ + public const MSG_ORGANIZATION_CLAIM_UNMATCHED = 'Token organization claim (`org_id` or `org_name`) is not allowed'; + + /** + * @var string + */ + public const MSG_ORGANIZATION_CLAIM_UNEXPECTED = 'Token organization claim (`org_id` or `org_name`) was not expected'; + public static function badSeparators( ?Throwable $previous = null, ): self { @@ -221,14 +231,6 @@ public static function mismatchedNonceClaim( return new self(sprintf(self::MSG_MISMATCHED_NONCE_CLAIM, $expected, $found), 0, $previous); } - public static function mismatchedOrgIdClaim( - string $expected, - string $found, - ?Throwable $previous = null, - ): self { - return new self(sprintf(self::MSG_MISMATCHED_ORG_ID_CLAIM, $expected, $found), 0, $previous); - } - public static function missingAlgHeader( ?Throwable $previous = null, ): self { @@ -283,12 +285,6 @@ public static function missingNonceClaim( return new self(self::MSG_MISSING_NONCE_CLAIM, 0, $previous); } - public static function missingOrgIdClaim( - ?Throwable $previous = null, - ): self { - return new self(self::MSG_MISSING_ORG_ID_CLAIM, 0, $previous); - } - public static function missingSubClaim( ?Throwable $previous = null, ): self { diff --git a/src/Token.php b/src/Token.php index e80bcf37..c541450c 100644 --- a/src/Token.php +++ b/src/Token.php @@ -166,6 +166,19 @@ public function getNonce(): ?string } public function getOrganization(): ?string + { + if (is_string($claim = $this->getOrganizationId())) { + return $claim; + } + + if (is_string($claim = $this->getOrganizationName())) { + return $claim; + } + + return null; + } + + public function getOrganizationId(): ?string { if (is_string($claim = $this->getParser()->getClaim('org_id'))) { return $claim; @@ -174,6 +187,15 @@ public function getOrganization(): ?string return null; } + public function getOrganizationName(): ?string + { + if (is_string($claim = $this->getParser()->getClaim('org_name'))) { + return $claim; + } + + return null; + } + public function getSubject(): ?string { if (is_string($claim = $this->getParser()->getClaim('sub'))) { diff --git a/src/Token/Validator.php b/src/Token/Validator.php index 4ebff333..7649eee0 100644 --- a/src/Token/Validator.php +++ b/src/Token/Validator.php @@ -6,6 +6,8 @@ use Auth0\SDK\Contract\Token\ValidatorInterface; +use Auth0\SDK\Exception\InvalidTokenException; + use function in_array; use function is_array; use function is_string; @@ -27,7 +29,7 @@ public function __construct( * * @param array $expects An array of allowed values for the 'aud' claim. Successful if ANY match. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function audience( array $expects, @@ -35,7 +37,7 @@ public function audience( $audience = $this->getClaim('aud'); if (null === $audience) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingAudienceClaim(); + throw InvalidTokenException::missingAudienceClaim(); } if (! is_array($audience)) { @@ -46,7 +48,7 @@ public function audience( return $this; } - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedAudClaim(implode(', ', $expects), implode(', ', $audience)); + throw InvalidTokenException::mismatchedAudClaim(implode(', ', $expects), implode(', ', $audience)); } /** @@ -54,7 +56,7 @@ public function audience( * * @param array $expects An array of allowed values for the 'azp' claim. Successful if ANY match. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function authorizedParty( array $expects, @@ -62,18 +64,18 @@ public function authorizedParty( $audience = $this->getClaim('aud'); if (null === $audience) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingAudienceClaim(); + throw InvalidTokenException::missingAudienceClaim(); } if (is_array($audience)) { $azp = $this->getClaim('azp'); if (null === $azp || ! is_string($azp)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingAzpClaim(); + throw InvalidTokenException::missingAzpClaim(); } if (! in_array($azp, $expects, true)) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedAzpClaim(implode(', ', $expects), $azp); + throw InvalidTokenException::mismatchedAzpClaim(implode(', ', $expects), $azp); } } @@ -87,7 +89,7 @@ public function authorizedParty( * @param int $leeway leeway in seconds to allow during time calculations * @param null|int $now Optional. Unix timestamp representing the current point in time to use for time calculations. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function authTime( int $maxAge, @@ -98,13 +100,13 @@ public function authTime( $now ??= time(); if (null === $authTime || ! is_numeric($authTime)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingAuthTimeClaim(); + throw InvalidTokenException::missingAuthTimeClaim(); } $validUntil = (int) $authTime + $maxAge + $leeway; if ($now > $validUntil) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedAuthTimeClaim($now, $validUntil); + throw InvalidTokenException::mismatchedAuthTimeClaim($now, $validUntil); } return $this; @@ -116,7 +118,7 @@ public function authTime( * @param int $leeway leeway in seconds to allow during time calculations * @param null|int $now Optional. Unix timestamp representing the current point in time to use for time calculations. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function expiration( int $leeway = 60, @@ -126,13 +128,13 @@ public function expiration( $now ??= time(); if (null === $expires || ! is_numeric($expires)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingExpClaim(); + throw InvalidTokenException::missingExpClaim(); } $expires = (int) $expires + $leeway; if ($now > $expires) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedExpClaim($now, $expires); + throw InvalidTokenException::mismatchedExpClaim($now, $expires); } return $this; @@ -141,14 +143,14 @@ public function expiration( /** * Validate the 'iat' claim is present. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function issued(): self { $issued = $this->getClaim('iat'); if (null === $issued) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingIatClaim(); + throw InvalidTokenException::missingIatClaim(); } return $this; @@ -159,7 +161,7 @@ public function issued(): self * * @param string $expects the value to compare with the claim * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function issuer( string $expects, @@ -167,11 +169,11 @@ public function issuer( $claim = $this->getClaim('iss'); if (null === $claim || ! is_string($claim)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingIssClaim(); + throw InvalidTokenException::missingIssClaim(); } if ($claim !== $expects) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedIssClaim($expects, $claim); + throw InvalidTokenException::mismatchedIssClaim($expects, $claim); } return $this; @@ -182,7 +184,7 @@ public function issuer( * * @param string $expects the value to compare with the claim * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function nonce( string $expects, @@ -190,34 +192,72 @@ public function nonce( $claim = $this->getClaim('nonce'); if (null === $claim || ! is_string($claim)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingNonceClaim(); + throw InvalidTokenException::missingNonceClaim(); } if ($claim !== $expects) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedNonceClaim($expects, $claim); + throw InvalidTokenException::mismatchedNonceClaim($expects, $claim); } return $this; } /** - * Validate the 'org_id' claim. + * Validate the 'org_id' and `org_name` claims. * - * @param array $expects An array of allowed values for the 'org_id' claim. Successful if ANY match. + * @param array $expects An array of allowed values for the 'org_id' or `org_name` claim. Successful if ANY match. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function organization( array $expects, ): self { - $claim = $this->getClaim('org_id'); + $allowedOrganizations = array_filter(array_values($expects)); + $organizationId = $this->getClaim('org_id'); + $organizationName = $this->getClaim('org_name'); - if (null === $claim || ! is_string($claim)) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingOrgIdClaim(); + // No claims or SDK allowlist configured, so skip validation. Pass. + if (['*'] === $allowedOrganizations) { + return $this; + } + + // If a claim is present, ensure it is a string; otherwise throw. + if ((null !== $organizationId && ! is_string($organizationId)) || (null !== $organizationName && ! is_string($organizationName))) { + throw new InvalidTokenException(InvalidTokenException::MSG_ORGANIZATION_CLAIM_BAD); + } + + // If an SDK allowlist has been configured, we need to run comparisons. + if ([] !== $allowedOrganizations) { + if (null === $organizationId && null === $organizationName) { + throw new InvalidTokenException(InvalidTokenException::MSG_ORGANIZATION_CLAIM_MISSING); + } + + if (null !== $organizationId) { + $allowedOrganizationIds = array_filter($allowedOrganizations, static fn ($org) => str_starts_with($org, 'org_')); + + // org_id claim is present and in the allowlist. Success. + if (null !== $organizationId && in_array($organizationId, $allowedOrganizationIds, true)) { + return $this; + } + } + + if (null !== $organizationName) { + // Normalize the org_name claim to lowercase for case insensitive comparisons. + $lowercaseOrganizationName = strtolower($organizationName); + $allowedOrganizationNames = array_map('strtolower', array_filter($allowedOrganizations, static fn ($org) => ! str_starts_with($org, 'org_'))); + + // org_name claim is present and in the allowlist. Success. + if (null !== $organizationName && in_array($lowercaseOrganizationName, $allowedOrganizationNames, true)) { + return $this; + } + } + + throw new InvalidTokenException(InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNMATCHED); } - if (! in_array($claim, $expects, true)) { - throw \Auth0\SDK\Exception\InvalidTokenException::mismatchedOrgIdClaim(implode(', ', $expects), $claim); + // A claim is present, but there is no allowlist configured. Throw. + if (null !== $organizationId || null !== $organizationName) { + throw new InvalidTokenException(InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNEXPECTED); } return $this; @@ -226,14 +266,14 @@ public function organization( /** * Validate the 'sub' claim is present. * - * @throws \Auth0\SDK\Exception\InvalidTokenException when claim validation fails + * @throws InvalidTokenException when claim validation fails */ public function subject(): self { $claim = $this->getClaim('sub'); if (null === $claim) { - throw \Auth0\SDK\Exception\InvalidTokenException::missingSubClaim(); + throw InvalidTokenException::missingSubClaim(); } return $this; diff --git a/tests/Unit/Auth0Test.php b/tests/Unit/Auth0Test.php index 0692d56d..f04f4b5d 100644 --- a/tests/Unit/Auth0Test.php +++ b/tests/Unit/Auth0Test.php @@ -428,7 +428,7 @@ public function defer( })->throws(InvalidTokenException::class); test('decode() compares `org_id` against `organization` configuration', function(): void { - $orgId = 'org8675309'; + $orgId = 'org_' . uniqid(); $token = (new TokenGenerator())->withHs256([ 'org_id' => $orgId, @@ -445,6 +445,42 @@ public function defer( expect($decoded->getOrganization())->toEqual($orgId); }); +test('decode() compares `org_name` against `organization` configuration', function(): void { + $orgName = uniqid(); + + $token = (new TokenGenerator())->withHs256([ + 'org_name' => $orgName, + 'iss' => 'https://' . $this->configuration['domain'] . '/' + ]); + + $auth0 = new Auth0($this->configuration + [ + 'tokenAlgorithm' => 'HS256', + 'organization' => [$orgName], + ]); + + $decoded = $auth0->decode($token); + + expect($decoded->getOrganization())->toEqual($orgName); +}); + +test('decode() does not match strings beginning with `org_` from the `organization` configuration against an `org_name` claim check', function(): void { + $orgName = 'org_' . uniqid(); + + $token = (new TokenGenerator())->withHs256([ + 'org_name' => $orgName, + 'iss' => 'https://' . $this->configuration['domain'] . '/' + ]); + + $auth0 = new Auth0($this->configuration + [ + 'tokenAlgorithm' => 'HS256', + 'organization' => [$orgName], + ]); + + $decoded = $auth0->decode($token); + + expect($decoded->getOrganization())->toEqual($orgName); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNMATCHED); + test('decode() throws an exception when `org_id` claim does not exist, but an `organization` is configured', function(): void { $token = (new TokenGenerator())->withHs256([ 'iss' => 'https://' . $this->configuration['domain'] . '/' @@ -452,15 +488,15 @@ public function defer( $auth0 = new Auth0($this->configuration + [ 'tokenAlgorithm' => 'HS256', - 'organization' => ['org8675309'], + 'organization' => ['org_' . uniqid()], ]); $auth0->decode($token); -})->throws(InvalidTokenException::class, InvalidTokenException::MSG_MISSING_ORG_ID_CLAIM); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_MISSING); test('decode() throws an exception when `org_id` does not match `organization` configuration', function(): void { - $expectedOrgId = uniqid(); - $tokenOrgId = uniqid(); + $expectedOrgId = 'org_' . uniqid(); + $tokenOrgId = 'org_' . uniqid(); $token = (new TokenGenerator())->withHs256([ 'org_id' => $tokenOrgId, @@ -473,7 +509,7 @@ public function defer( ]); $auth0->decode($token); -})->throws(InvalidTokenException::class); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNMATCHED); test('decode() can be used with access tokens', function (): void { $token = (new TokenGenerator())->withHs256([ diff --git a/tests/Unit/Token/ValidatorTest.php b/tests/Unit/Token/ValidatorTest.php index a155f00e..8d7d2e6f 100644 --- a/tests/Unit/Token/ValidatorTest.php +++ b/tests/Unit/Token/ValidatorTest.php @@ -16,7 +16,7 @@ 'auth_time' => time() - 100, 'exp' => time() + 1000, 'iat' => time() - 1000, - 'azp' => uniqid() + 'azp' => uniqid(), ]; }); @@ -92,3 +92,42 @@ unset($this->claims['sub']); (new Validator($this->claims))->subject(); })->throws(InvalidTokenException::class, InvalidTokenException::MSG_MISSING_SUB_CLAIM); + +test('organization() throws an exception when a `org_id` claim is expected but not found', function(): void { + (new Validator($this->claims))->organization(['org_123']); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_MISSING); + +test('organization() throws an exception when a `org_name` claim is expected but not found', function(): void { + (new Validator($this->claims))->organization(['organizationTesting123']); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_MISSING); + +test('organization() does not throw an exception when wildcard organizations are configured', function(): void { + $this->claims['org_id'] = uniqid(); + $validator = (new Validator($this->claims))->organization(['*']); + expect($validator)->toBeInstanceOf(Validator::class); +}); + +test('organization() throws an exception when either a `org_id` claim is an unexpected type', function(): void { + $this->claims['org_id'] = true; + (new Validator($this->claims))->organization(['org_123']); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_BAD); + +test('organization() throws an exception when either a `org_name` claim is an unexpected type', function(): void { + $this->claims['org_name'] = true; + (new Validator($this->claims))->organization(['organizationTesting123']); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_BAD); + +test('organization() throws an exception when an unexpected `org_id` claim is encountered', function(): void { + $this->claims['org_id'] = uniqid(); + (new Validator($this->claims))->organization([]); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNEXPECTED); + +test('organization() throws an exception when an unexpected `org_name` claim is encountered', function(): void { + $this->claims['org_name'] = uniqid(); + (new Validator($this->claims))->organization([]); +})->throws(InvalidTokenException::class, InvalidTokenException::MSG_ORGANIZATION_CLAIM_UNEXPECTED); + +test('organization() does not throw an exception when there are no organization claims and no allowlist configured', function(): void { + $validator = (new Validator($this->claims))->organization([]); + expect($validator)->toBeInstanceOf(Validator::class); +}); diff --git a/tests/Unit/TokenTest.php b/tests/Unit/TokenTest.php index 4a8dff63..2628c35a 100644 --- a/tests/Unit/TokenTest.php +++ b/tests/Unit/TokenTest.php @@ -155,7 +155,7 @@ function(): SdkConfiguration { array $claims ): void { $token = new Token($configuration, $jwt->token, Token::TYPE_ID_TOKEN); - expect($token->validate(null, null, ['__test_org__'], $claims['nonce'], 100))->toEqual($token); + expect($token->validate(null, null, ['org_123'], $claims['nonce'], 100))->toEqual($token); })->with(['mocked data' => [ function(): SdkConfiguration { $this->configuration->setDomain('domain.test'); @@ -164,7 +164,7 @@ function(): SdkConfiguration { $this->configuration->setClientSecret('__test_client_secret__'); return $this->configuration; }, - fn() => TokenGenerator::create(TokenGenerator::TOKEN_ID, TokenGenerator::ALG_HS256, ['org_id' => '__test_org__']), + fn() => TokenGenerator::create(TokenGenerator::TOKEN_ID, TokenGenerator::ALG_HS256, ['org_id' => 'org_123']), fn() => ['nonce' => '__test_nonce__'] ]]); From f5eb66f7dff8d730edb8541e990635583b20c09b Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Mon, 17 Jul 2023 22:55:46 -0500 Subject: [PATCH 2/6] Update Token.php --- src/Token.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Token.php b/src/Token.php index c541450c..e5c3bf41 100644 --- a/src/Token.php +++ b/src/Token.php @@ -167,11 +167,11 @@ public function getNonce(): ?string public function getOrganization(): ?string { - if (is_string($claim = $this->getOrganizationId())) { + if (null !== ($claim = $this->getOrganizationId())) { return $claim; } - if (is_string($claim = $this->getOrganizationName())) { + if (null !== ($claim = $this->getOrganizationName())) { return $claim; } From bb2ff889d7854efda1416905088a32fbccce01f2 Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Mon, 17 Jul 2023 23:00:03 -0500 Subject: [PATCH 3/6] Update InvalidTokenException.php --- src/Exception/InvalidTokenException.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Exception/InvalidTokenException.php b/src/Exception/InvalidTokenException.php index aad826ab..63652674 100644 --- a/src/Exception/InvalidTokenException.php +++ b/src/Exception/InvalidTokenException.php @@ -115,42 +115,42 @@ final class InvalidTokenException extends Exception implements Auth0Exception /** * @var string */ - public const MSG_REQUIRES_CLIENT_SECRET = 'Cannot verify signature: Client secret must be configured to verify HS256 signatures'; + public const MSG_ORGANIZATION_CLAIM_BAD = 'Token organization claim (`org_id` or `org_name`) must be a string'; /** * @var string */ - public const MSG_REQUIRES_JWKS_URI = 'Cannot verify signature: JWKS uri was not configured properly'; + public const MSG_ORGANIZATION_CLAIM_MISSING = 'Token organization claim (`org_id` or `org_name`) was not found'; /** * @var string */ - public const MSG_UNEXPECTED_SIGNING_ALGORITHM = 'Expected token signed with "%s" algorithm, but token uses "%s"'; + public const MSG_ORGANIZATION_CLAIM_UNEXPECTED = 'Token organization claim (`org_id` or `org_name`) was not expected'; /** * @var string */ - public const MSG_UNSUPPORTED_SIGNING_ALGORITHM = 'Signature algorithm of "%s" is not supported. Expected the token to be signed with "RS256" or "HS256"'; + public const MSG_ORGANIZATION_CLAIM_UNMATCHED = 'Token organization claim (`org_id` or `org_name`) is not allowed'; /** * @var string */ - public const MSG_ORGANIZATION_CLAIM_BAD = 'Token organization claim (`org_id` or `org_name`) must be a string'; + public const MSG_REQUIRES_CLIENT_SECRET = 'Cannot verify signature: Client secret must be configured to verify HS256 signatures'; /** * @var string */ - public const MSG_ORGANIZATION_CLAIM_MISSING = 'Token organization claim (`org_id` or `org_name`) was not found'; + public const MSG_REQUIRES_JWKS_URI = 'Cannot verify signature: JWKS uri was not configured properly'; /** * @var string */ - public const MSG_ORGANIZATION_CLAIM_UNMATCHED = 'Token organization claim (`org_id` or `org_name`) is not allowed'; + public const MSG_UNEXPECTED_SIGNING_ALGORITHM = 'Expected token signed with "%s" algorithm, but token uses "%s"'; /** * @var string */ - public const MSG_ORGANIZATION_CLAIM_UNEXPECTED = 'Token organization claim (`org_id` or `org_name`) was not expected'; + public const MSG_UNSUPPORTED_SIGNING_ALGORITHM = 'Signature algorithm of "%s" is not supported. Expected the token to be signed with "RS256" or "HS256"'; public static function badSeparators( ?Throwable $previous = null, From af9eb917bfd1ddf841029b16a5c3fbab10cf62e5 Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Mon, 17 Jul 2023 23:00:05 -0500 Subject: [PATCH 4/6] Update Validator.php --- src/Token/Validator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Token/Validator.php b/src/Token/Validator.php index 7649eee0..e85d71dc 100644 --- a/src/Token/Validator.php +++ b/src/Token/Validator.php @@ -233,10 +233,10 @@ public function organization( } if (null !== $organizationId) { - $allowedOrganizationIds = array_filter($allowedOrganizations, static fn ($org) => str_starts_with($org, 'org_')); + $allowedOrganizationIds = array_filter($allowedOrganizations, static fn ($org): bool => str_starts_with($org, 'org_')); // org_id claim is present and in the allowlist. Success. - if (null !== $organizationId && in_array($organizationId, $allowedOrganizationIds, true)) { + if (in_array($organizationId, $allowedOrganizationIds, true)) { return $this; } } @@ -244,10 +244,10 @@ public function organization( if (null !== $organizationName) { // Normalize the org_name claim to lowercase for case insensitive comparisons. $lowercaseOrganizationName = strtolower($organizationName); - $allowedOrganizationNames = array_map('strtolower', array_filter($allowedOrganizations, static fn ($org) => ! str_starts_with($org, 'org_'))); + $allowedOrganizationNames = array_map('strtolower', array_filter($allowedOrganizations, static fn ($org): bool => ! str_starts_with($org, 'org_'))); // org_name claim is present and in the allowlist. Success. - if (null !== $organizationName && in_array($lowercaseOrganizationName, $allowedOrganizationNames, true)) { + if (in_array($lowercaseOrganizationName, $allowedOrganizationNames, true)) { return $this; } } From e5cea6f37d0ff6cffb77ead63d5ca113aa21297d Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Mon, 17 Jul 2023 23:02:22 -0500 Subject: [PATCH 5/6] Update Validator.php --- src/Token/Validator.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Token/Validator.php b/src/Token/Validator.php index aea4b8ef..7909659e 100644 --- a/src/Token/Validator.php +++ b/src/Token/Validator.php @@ -7,8 +7,6 @@ use Auth0\SDK\Contract\Token\ValidatorInterface; use Auth0\SDK\Exception\InvalidTokenException; -use Auth0\SDK\Exception\InvalidTokenException; - use function in_array; use function is_array; use function is_string; From e87a06b96bf5e992467b21dc7e4eb112b731599a Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Tue, 18 Jul 2023 12:18:52 -0500 Subject: [PATCH 6/6] Address feedback by @stevehobbsdev --- src/Token/Validator.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Token/Validator.php b/src/Token/Validator.php index 7909659e..9dd854a6 100644 --- a/src/Token/Validator.php +++ b/src/Token/Validator.php @@ -241,12 +241,10 @@ public function organization( } if (null !== $organizationName) { - // Normalize the org_name claim to lowercase for case insensitive comparisons. - $lowercaseOrganizationName = strtolower($organizationName); $allowedOrganizationNames = array_map('strtolower', array_filter($allowedOrganizations, static fn ($org): bool => ! str_starts_with($org, 'org_'))); // org_name claim is present and in the allowlist. Success. - if (in_array($lowercaseOrganizationName, $allowedOrganizationNames, true)) { + if (in_array($organizationName, $allowedOrganizationNames, true)) { return $this; } }