From a8f35bbbd85b84c3b4a9b9aac68d70d7708beb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Fri, 25 Jun 2021 08:38:58 +0200 Subject: [PATCH 01/14] Updated todo list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cc54cb1..d1ce548 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ OK (1 test, 1 assertion) ## Todo It is welcome to open a pull request with a fix of any issue: +- [ ] Upgrade `phpseclib/phpseclib` to version `3.0.7` - [ ] Upgrade `lcobucci/jwt` to version `4.x`. Reported in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) - [x] Make library compatible with PHP `7.4.3`. Reported in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) - [ ] Make library compatible with PHP `8.0.0` From 51f567967615cdce455af3642e89dad7a6a849cf Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 11:32:25 +0200 Subject: [PATCH 02/14] Upgraded `lcobucci/jwt` to version 4.x --- composer.json | 2 +- src/Api/AppleApiClient.php | 8 +- src/Api/AppleApiClientInterface.php | 13 +++ src/Api/Enum/CryptographicAlgorithmEnum.php | 2 +- .../Exception/InvalidResponseException.php | 2 +- .../PublicKeyFetchingFailedException.php | 2 +- .../Exception/ResponseValidationException.php | 2 +- ...pportedCryptographicAlgorithmException.php | 2 +- ...InvalidCryptographicAlgorithmException.php | 2 +- src/Auth/Exception/InvalidJwtException.php | 2 +- .../Exception/KeysFetchingFailedException.php | 2 +- src/Auth/Exception/MissingClaimException.php | 2 +- .../Exception/NotSignedTokenException.php | 9 -- .../Exception/ValidationFailedException.php | 2 +- .../Exception/VerificationFailedException.php | 2 +- src/Auth/Factory/AppleJwtStructFactory.php | 37 +++--- src/Auth/Jwt/JwtValidator.php | 17 ++- src/Auth/Jwt/JwtVerifier.php | 28 +++-- src/Auth/Service/AppleJwtFetchingService.php | 3 +- .../AppleJwtFetchingServiceInterface.php | 19 +++ src/Auth/Struct/JwtPayload.php | 22 ++-- .../E2e/Auth/AppleJwtFetchingServiceTest.php | 26 +++-- tests/Unit/Api/AppleApiClientTest.php | 2 +- .../Unit/Api/Factory/ResponseFactoryTest.php | 2 +- .../Factory/AppleJwtStructFactoryTest.php | 83 +++++-------- tests/Unit/Auth/Jwt/JwtParserTest.php | 4 +- tests/Unit/Auth/Jwt/JwtValidatorTest.php | 44 ++++--- tests/Unit/Auth/Jwt/JwtVerifierTest.php | 110 +++++------------- .../Service/AppleJwtFetchingServiceTest.php | 14 ++- 29 files changed, 225 insertions(+), 240 deletions(-) create mode 100644 src/Api/AppleApiClientInterface.php delete mode 100644 src/Auth/Exception/NotSignedTokenException.php create mode 100644 src/Auth/Service/AppleJwtFetchingServiceInterface.php diff --git a/composer.json b/composer.json index 4496ab2..ce1e23d 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "ext-mbstring": "*", "ext-openssl": "*", "phpseclib/phpseclib": "^2.0", - "lcobucci/jwt": "3.3.3", + "lcobucci/jwt": "4.0", "guzzlehttp/guzzle": "^6.0|^7.0" }, "require-dev": { diff --git a/src/Api/AppleApiClient.php b/src/Api/AppleApiClient.php index 6e7b358..42709f1 100644 --- a/src/Api/AppleApiClient.php +++ b/src/Api/AppleApiClient.php @@ -5,8 +5,10 @@ use Azimo\Apple\Api\Exception\PublicKeyFetchingFailedException; use Azimo\Apple\Api\Factory\ResponseFactory; use GuzzleHttp; +use GuzzleHttp\Utils; +use InvalidArgumentException; -class AppleApiClient +final class AppleApiClient implements AppleApiClientInterface { /** * @var GuzzleHttp\ClientInterface @@ -34,9 +36,9 @@ public function getAuthKeys(): Response\JsonWebKeySetCollection try { return $this->responseFactory->createFromArray( - GuzzleHttp\json_decode($response->getBody()->getContents(), true) + Utils::jsonDecode($response->getBody()->getContents(), true) ); - } catch (\InvalidArgumentException $exception) { + } catch (InvalidArgumentException $exception) { throw new Exception\InvalidResponseException( 'Unable to decode response', $exception->getCode(), diff --git a/src/Api/AppleApiClientInterface.php b/src/Api/AppleApiClientInterface.php new file mode 100644 index 0000000..1aa3769 --- /dev/null +++ b/src/Api/AppleApiClientInterface.php @@ -0,0 +1,13 @@ +getClaim('iss'), - $token->getClaim('aud'), - $token->getClaim('exp'), - $token->getClaim('iat'), - $token->getClaim('sub'), - $token->getClaim('c_hash'), - $token->getClaim('email', ''), - // For some reason Apple API returns boolean flag as a string - (string) $token->getClaim('email_verified', 'false') === 'true', - // For some reason Apple API returns boolean flag as a string - (string) $token->getClaim('is_private_email', 'false') === 'true', - $token->getClaim('auth_time'), - $token->getClaim('nonce_supported', false) - ); - } catch (OutOfBoundsException $exception) { - throw new MissingClaimException($exception->getMessage(), $exception->getCode(), $exception); - } + $claims = $token->claims(); + + return new JwtPayload( + $claims->get('iss'), + $claims->get('aud'), + $claims->get('exp'), + $claims->get('iat'), + $claims->get('sub'), + $claims->get('c_hash', ''), + $claims->get('email', ''), + // For some reason Apple API returns boolean flag as a string + (string) $claims->get('email_verified', 'false') === 'true', + // For some reason Apple API returns boolean flag as a string + (string) $claims->get('is_private_email', 'false') === 'true', + $claims->get('auth_time'), + $claims->get('nonce_supported', false) + ); } } diff --git a/src/Auth/Jwt/JwtValidator.php b/src/Auth/Jwt/JwtValidator.php index b72635b..153b95b 100644 --- a/src/Auth/Jwt/JwtValidator.php +++ b/src/Auth/Jwt/JwtValidator.php @@ -2,22 +2,29 @@ namespace Azimo\Apple\Auth\Jwt; +use DateTimeImmutable; use Lcobucci\JWT; class JwtValidator { /** - * @var JWT\ValidationData + * @var JWT\Validator */ - private $validationData; + private $validator; - public function __construct(JWT\ValidationData $validationData) + /** + * @var array + */ + private $constraints; + + public function __construct(JWT\Validator $validator, array $constraints) { - $this->validationData = $validationData; + $this->validator = $validator; + $this->constraints = $constraints; } public function isValid(JWT\Token $jwt): bool { - return $jwt->validate($this->validationData) && !$jwt->isExpired(); + return $this->validator->validate($jwt, ...$this->constraints) && !$jwt->isExpired(new DateTimeImmutable()); } } diff --git a/src/Auth/Jwt/JwtVerifier.php b/src/Auth/Jwt/JwtVerifier.php index 31cf840..fee074d 100644 --- a/src/Auth/Jwt/JwtVerifier.php +++ b/src/Auth/Jwt/JwtVerifier.php @@ -2,11 +2,10 @@ namespace Azimo\Apple\Auth\Jwt; -use Azimo\Apple\Api\AppleApiClient; +use Azimo\Apple\Api\AppleApiClientInterface; use Azimo\Apple\Api\Exception as ApiException; use Azimo\Apple\Api\Response\JsonWebKeySet; use Azimo\Apple\Auth\Exception; -use BadMethodCallException; use Lcobucci\JWT; use OutOfBoundsException; use phpseclib\Crypt\RSA; @@ -15,7 +14,7 @@ class JwtVerifier { /** - * @var AppleApiClient + * @var AppleApiClientInterface */ private $client; @@ -29,27 +28,34 @@ class JwtVerifier */ private $signer; - public function __construct(AppleApiClient $client, RSA $rsa, JWT\Signer $signer) + /** + * @var JWT\Validator + */ + private $validator; + + public function __construct(AppleApiClientInterface $client, JWT\Validator $validator, RSA $rsa, JWT\Signer $signer) { $this->client = $client; $this->rsa = $rsa; $this->signer = $signer; + $this->validator = $validator; } /** * @throws Exception\InvalidCryptographicAlgorithmException * @throws Exception\KeysFetchingFailedException - * @throws Exception\NotSignedTokenException */ public function verify(JWT\Token $jwt): bool { $this->loadRsaKey($this->getAuthKey($jwt)); - try { - return $jwt->verify($this->signer, $this->rsa->getPublicKey()); - } catch (BadMethodCallException $exception) { - throw new Exception\NotSignedTokenException($exception->getMessage(), $exception->getCode(), $exception); - } + return $this->validator->validate( + $jwt, + new JWT\Validation\Constraint\SignedWith( + $this->signer, + JWT\Signer\Key\InMemory::plainText($this->rsa->getPublicKey()) + ) + ); } /** @@ -69,7 +75,7 @@ private function getAuthKey(JWT\Token $jwt): JsonWebKeySet } try { - $cryptographicAlgorithm = $jwt->getHeader('kid'); + $cryptographicAlgorithm = $jwt->headers()->get('kid'); $authKey = $authKeys->getByCryptographicAlgorithm($cryptographicAlgorithm); } catch (OutOfBoundsException | ApiException\UnsupportedCryptographicAlgorithmException $exception) { throw new Exception\InvalidCryptographicAlgorithmException( diff --git a/src/Auth/Service/AppleJwtFetchingService.php b/src/Auth/Service/AppleJwtFetchingService.php index 8403eac..638c7b2 100644 --- a/src/Auth/Service/AppleJwtFetchingService.php +++ b/src/Auth/Service/AppleJwtFetchingService.php @@ -7,7 +7,7 @@ use Azimo\Apple\Auth\Jwt; use Azimo\Apple\Auth\Struct\JwtPayload; -class AppleJwtFetchingService +final class AppleJwtFetchingService implements AppleJwtFetchingServiceInterface { /** * @var Jwt\JwtParser @@ -46,7 +46,6 @@ public function __construct( * @throws Exception\InvalidJwtException * @throws Exception\KeysFetchingFailedException * @throws Exception\MissingClaimException - * @throws Exception\NotSignedTokenException * @throws Exception\ValidationFailedException * @throws Exception\VerificationFailedException */ diff --git a/src/Auth/Service/AppleJwtFetchingServiceInterface.php b/src/Auth/Service/AppleJwtFetchingServiceInterface.php new file mode 100644 index 0000000..c1aefc1 --- /dev/null +++ b/src/Auth/Service/AppleJwtFetchingServiceInterface.php @@ -0,0 +1,19 @@ +iss; } - public function getAud(): string + public function getAud(): array { return $this->aud; } - public function getExp(): int + public function getExp(): DateTimeInterface { return $this->exp; } - public function getIat(): int + public function getIat§(): DateTimeInterface { return $this->iat; } diff --git a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php index c9e1ed0..c621a2d 100644 --- a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php +++ b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php @@ -5,13 +5,16 @@ use Azimo\Apple\Api; use Azimo\Apple\Auth; use GuzzleHttp; -use Lcobucci\JWT\Parser; +use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer\Rsa\Sha256; -use Lcobucci\JWT\ValidationData; +use Lcobucci\JWT\Token\Parser; +use Lcobucci\JWT\Validation\Constraint\IssuedBy; +use Lcobucci\JWT\Validation\Constraint\PermittedFor; +use Lcobucci\JWT\Validation\Validator; use Mockery\Adapter\Phpunit\MockeryTestCase; use phpseclib\Crypt\RSA; -class AppleJwtFetchingServiceTest extends MockeryTestCase +final class AppleJwtFetchingServiceTest extends MockeryTestCase { /** * @var Auth\Service\AppleJwtFetchingService @@ -22,12 +25,8 @@ public function setUp(): void { parent::setUp(); - $validationData = new ValidationData(); - $validationData->setIssuer('https://appleid.apple.com'); - $validationData->setAudience('com.c.azimo.stage'); - $this->appleJwtFetchingService = new Auth\Service\AppleJwtFetchingService( - new Auth\Jwt\JwtParser(new Parser()), + new Auth\Jwt\JwtParser(new Parser(new JoseEncoder())), new Auth\Jwt\JwtVerifier( new Api\AppleApiClient( new GuzzleHttp\Client( @@ -39,10 +38,17 @@ public function setUp(): void ), new Api\Factory\ResponseFactory() ), + new Validator(), new RSA(), new Sha256() ), - new Auth\Jwt\JwtValidator($validationData), + new Auth\Jwt\JwtValidator( + new Validator(), + [ + new IssuedBy('https://appleid.apple.com'), + new PermittedFor('com.c.azimo.stage'), + ] + ), new Auth\Factory\AppleJwtStructFactory() ); } @@ -50,7 +56,7 @@ public function setUp(): void public function testIfGetJwtPayloadReturnExpectedJwtPayload(): void { $jwtPayload = $this->appleJwtFetchingService->getJwtPayload( - 'eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MTMyMTIzNjIsImlhdCI6MTYxMzEyNTk2Miwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJ4SGpPV24zblpUa3JTS1dRSGRRZmFBIiwiYXV0aF90aW1lIjoxNjEzMTI1OTYyLCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.YShVEmo-QGDnMxU_M9wkwOFcqC5vqvMvXDDlZvQ1VO-WA74_CYOBMbdMKvvTWWGgpvnykNVduvixuFkv_3vpRo2llydwllmVJtMxshTx-kIDmBnInP03lP2jdaDSonDmm0UiXtGEmOqqeFiT_sgUn5o0jfUUreNrXMBM9eLpzEDcjyMW_u3qBhds2SQlsJew6Hd9w16lMTngrJYrMq2H6gogWaCqoXdXexJGdQYfBiX2J14XEgGAyW_7ZupFKT0YCb_OwBQubocsdKbRiw7KlHZVH4vcCaz6e5as9Z-g9V8o4eOFMhuYaugmuGdpBruulyOgDgdPYmR3JCMdPeLFdA' + 'eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MjQ2OTI0MTgsImlhdCI6MTYyNDYwNjAxOCwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJrZUVNV0dadGg5aDdDRUhIR1dtSVRRIiwiYXV0aF90aW1lIjoxNjI0NjA2MDE4LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.DQNhKgtNXGffuelanmMT_lnUPbIAVVEiDuj7NL-H4nusxZ5-lK5UBgWhCj79PX1NUxQKj2bOqcb2R2oE2POxIrkpm1jPSt5QXaBeBmwdKx6NtXss2BOq0TL8Jlp7N5UW2TSC2Dk3Cu8-WC-gQf9jgtnzsEkpJFO1G6XrG6ZWCLATDcinN2XRHKzbmxiwNdykUhi1EzH4ug5e0XWchdY5h8QeYqmP0K7SqXZZPxxmcWZC_9g02H58tgChrsYzDezOQxwrf08w7jDXKbaqlpThfh9FMNsMIGaFekhSneOth5_TSc-CPqSHdq3ORRqbiERWcqi0FGJmPnN8-oBVBkcEQA' ); self::assertInstanceOf(Auth\Struct\JwtPayload::class, $jwtPayload); diff --git a/tests/Unit/Api/AppleApiClientTest.php b/tests/Unit/Api/AppleApiClientTest.php index ea8020d..deb344e 100644 --- a/tests/Unit/Api/AppleApiClientTest.php +++ b/tests/Unit/Api/AppleApiClientTest.php @@ -9,7 +9,7 @@ use Mockery\Adapter\Phpunit\MockeryTestCase; use Psr\Http\Message\RequestInterface; -class AppleApiClientTest extends MockeryTestCase +final class AppleApiClientTest extends MockeryTestCase { /** * @var Api\AppleApiClient diff --git a/tests/Unit/Api/Factory/ResponseFactoryTest.php b/tests/Unit/Api/Factory/ResponseFactoryTest.php index 62457f1..20cbd82 100644 --- a/tests/Unit/Api/Factory/ResponseFactoryTest.php +++ b/tests/Unit/Api/Factory/ResponseFactoryTest.php @@ -8,7 +8,7 @@ use Azimo\Apple\Api\Response\JsonWebKeySetCollection; use Mockery\Adapter\Phpunit\MockeryTestCase; -class ResponseFactoryTest extends MockeryTestCase +final class ResponseFactoryTest extends MockeryTestCase { /** * @var ResponseFactory diff --git a/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php b/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php index fa37b77..cf27f12 100644 --- a/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php +++ b/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php @@ -2,13 +2,13 @@ namespace Azimo\Apple\Tests\Unit\Auth\Factory; -use Azimo\Apple\Auth\Exception\MissingClaimException; use Azimo\Apple\Auth\Factory\AppleJwtStructFactory; use Azimo\Apple\Auth\Struct\JwtPayload; -use Lcobucci\JWT\Claim\EqualsTo; +use DateTimeImmutable; use Lcobucci\JWT\Token; +use Mockery\Adapter\Phpunit\MockeryTestCase; -class AppleJwtStructFactoryTest extends \Mockery\Adapter\Phpunit\MockeryTestCase +final class AppleJwtStructFactoryTest extends MockeryTestCase { /** * @var AppleJwtStructFactory @@ -24,12 +24,14 @@ protected function setUp(): void public function testIfCreateJwtPayloadFromTokenReturnsExpectedJsonPayload(): void { - $this->assertEquals( + $currentDate = new DateTimeImmutable(); + + self::assertEquals( new JwtPayload( 'https://appleid.apple.com', - 'com.acme.app', - 1591622611, - 1591622011, + ['com.acme.app'], + $currentDate, + $currentDate, 'foo.bar.baz', 'qGzMhtsfTCom-bl1PJYLHk', 'foo@privaterelay.appleid.com', @@ -39,52 +41,31 @@ public function testIfCreateJwtPayloadFromTokenReturnsExpectedJsonPayload(): voi true ), $this->appleJwtStructFactory->createJwtPayloadFromToken( - new Token( - [ - 'kid' => 'eXaunmL', - 'alg' => 'RS256', - ], - [ - 'iss' => new EqualsTo('iss', 'https://appleid.apple.com'), - 'aud' => new EqualsTo('aud', 'com.acme.app'), - 'exp' => new EqualsTo('exp', 1591622611), - 'iat' => new EqualsTo('iat', 1591622011), - 'sub' => new EqualsTo('sub', 'foo.bar.baz'), - 'c_hash' => new EqualsTo('c_hash', 'qGzMhtsfTCom-bl1PJYLHk'), - 'email' => new EqualsTo('email', 'foo@privaterelay.appleid.com'), - 'email_verified' => new EqualsTo('email_verified', 'true'), - 'is_private_email' => new EqualsTo('is_private_email', 'true'), - 'auth_time' => new EqualsTo('auth_time', 1591622011), - 'nonce_supported' => new EqualsTo('nonce_supported', true), - ] + new Token\Plain( + new Token\DataSet( + [ + 'kid' => 'eXaunmL', + 'alg' => 'RS256', + ], '' + ), + new Token\DataSet( + [ + 'iss' => 'https://appleid.apple.com', + 'aud' => ['com.acme.app'], + 'exp' => $currentDate, + 'iat' => $currentDate, + 'sub' => 'foo.bar.baz', + 'c_hash' => 'qGzMhtsfTCom-bl1PJYLHk', + 'email' => 'foo@privaterelay.appleid.com', + 'email_verified' => 'true', + 'is_private_email' => 'true', + 'auth_time' => 1591622011, + 'nonce_supported' => true, + ], '' + ), + Token\Signature::fromEmptyData() ) ) ); } - - public function testIfCreateJwtPayloadFromTokenThrowsMissingClaimExceptionWhenAnyClaimIsMissing(): void - { - $this->expectExceptionMessage('Requested claim is not configured'); - $this->expectException(MissingClaimException::class); - $this->appleJwtStructFactory->createJwtPayloadFromToken( - new Token( - [ - 'kid' => 'eXaunmL', - 'alg' => 'RS256', - ], - [ - 'iss' => new EqualsTo('iss', 'https://appleid.apple.com'), - 'aud' => new EqualsTo('aud', 'com.acme.app'), - 'exp' => new EqualsTo('exp', 1591622611), - 'iat' => new EqualsTo('iat', 1591622011), - 'sub' => new EqualsTo('sub', 'foo.bar.baz'), - 'email' => new EqualsTo('email', 'foo@privaterelay.appleid.com'), - 'email_verified' => new EqualsTo('email_verified', 'true'), - 'is_private_email' => new EqualsTo('is_private_email', 'true'), - 'auth_time' => new EqualsTo('auth_time', 1591622011), - 'nonce_supported' => new EqualsTo('nonce_supported', true), - ] - ) - ); - } } diff --git a/tests/Unit/Auth/Jwt/JwtParserTest.php b/tests/Unit/Auth/Jwt/JwtParserTest.php index e8ee970..a8026d4 100644 --- a/tests/Unit/Auth/Jwt/JwtParserTest.php +++ b/tests/Unit/Auth/Jwt/JwtParserTest.php @@ -11,7 +11,7 @@ use Mockery\Adapter\Phpunit\MockeryTestCase; use RuntimeException; -class JwtParserTest extends MockeryTestCase +final class JwtParserTest extends MockeryTestCase { /** * @var Parser|Mockery\MockInterface @@ -34,7 +34,7 @@ protected function setUp(): void public function testIfParseReturnsExpectedTokenObjectWhenJwtIsCorrect(): void { - $token = new Token(); + $token = Mockery::mock(Token::class); $this->jwtParserMock->shouldReceive('parse') ->once() diff --git a/tests/Unit/Auth/Jwt/JwtValidatorTest.php b/tests/Unit/Auth/Jwt/JwtValidatorTest.php index 2430ad9..c90e0e1 100644 --- a/tests/Unit/Auth/Jwt/JwtValidatorTest.php +++ b/tests/Unit/Auth/Jwt/JwtValidatorTest.php @@ -3,46 +3,56 @@ namespace Azimo\Apple\Tests\Unit\Auth\Jwt; use Azimo\Apple\Auth\Jwt\JwtValidator; +use DateTimeImmutable; use Lcobucci\JWT; use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; -class JwtValidatorTest extends MockeryTestCase +final class JwtValidatorTest extends MockeryTestCase { /** - * @var JWT\ValidationData|Mockery\MockInterface + * @var JWT\Token|Mockery\MockInterface */ - private $validationDataMock; + private $jwtMock; /** - * @var JWT\Token|Mockery\MockInterface + * @var */ - private $jwtMock; + private $constraints; /** - * @var JwtValidator + * @var JWT\Validator|Mockery\MockInterface + */ + private $validatorMock; + + /** + * @var JwtValidator JwtValidator */ private $jwtValidator; + /** + * @var JwtValidator JwtValidator + */ + protected function setUp(): void { parent::setUp(); - $this->validationDataMock = Mockery::mock(JWT\ValidationData::class); $this->jwtMock = Mockery::mock(JWT\Token::class); - - $this->jwtValidator = new JwtValidator($this->validationDataMock); + $this->constraints = []; + $this->validatorMock = Mockery::mock(JWT\Validator::class); + $this->jwtValidator = new JwtValidator($this->validatorMock, $this->constraints); } public function testIfIsValidReturnsTrueWhenTokenDataAreValidAndTokenIsNotExpired(): void { - $this->jwtMock->shouldReceive('validate') + $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->validationDataMock) + ->with($this->jwtMock, ...$this->constraints) ->andReturn(true); $this->jwtMock->shouldReceive('isExpired') ->once() - ->withNoArgs() + ->with(DateTimeImmutable::class) ->andReturn(false); self::assertTrue($this->jwtValidator->isValid($this->jwtMock)); @@ -50,13 +60,13 @@ public function testIfIsValidReturnsTrueWhenTokenDataAreValidAndTokenIsNotExpire public function testIfIsValidReturnsFalseWhenTokenDataAreValidAndTokenIsExpired(): void { - $this->jwtMock->shouldReceive('validate') + $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->validationDataMock) + ->with($this->jwtMock, ...$this->constraints) ->andReturn(true); $this->jwtMock->shouldReceive('isExpired') ->once() - ->withNoArgs() + ->with(DateTimeImmutable::class) ->andReturn(true); self::assertFalse($this->jwtValidator->isValid($this->jwtMock)); @@ -64,9 +74,9 @@ public function testIfIsValidReturnsFalseWhenTokenDataAreValidAndTokenIsExpired( public function testIfIsValidReturnsFalseWhenTokenDataAreInvalidValidAndTokenIsNotExpired(): void { - $this->jwtMock->shouldReceive('validate') + $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->validationDataMock) + ->with($this->jwtMock, ...$this->constraints) ->andReturn(false); $this->jwtMock->shouldNotReceive('isExpired'); diff --git a/tests/Unit/Auth/Jwt/JwtVerifierTest.php b/tests/Unit/Auth/Jwt/JwtVerifierTest.php index 4647b24..095d7cc 100644 --- a/tests/Unit/Auth/Jwt/JwtVerifierTest.php +++ b/tests/Unit/Auth/Jwt/JwtVerifierTest.php @@ -5,7 +5,6 @@ use Azimo\Apple\APi; use Azimo\Apple\Auth\Exception; use Azimo\Apple\Auth\Jwt\JwtVerifier; -use BadMethodCallException; use Lcobucci\JWT; use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -26,14 +25,14 @@ class JwtVerifierTest extends MockeryTestCase private $rsaMock; /** - * @var JWT\Signer\Rsa\Sha256|Mockery\MockInterface + * @var JWT\Validator|Mockery\MockInterface */ - private $signerMock; + private $jwtMock; /** - * @var JWT\Token|Mockery\MockInterface + * @var JWT\Validator|Mockery\MockInterface */ - private $jwtMock; + private $validatorMock; /** * @var JwtVerifier @@ -44,12 +43,17 @@ protected function setUp(): void { parent::setUp(); - $this->appleApiClient = Mockery::mock(Api\AppleApiClient::class); + $this->validatorMock = Mockery::mock(JWT\Validator::class); + $this->appleApiClient = Mockery::mock(Api\AppleApiClientInterface::class); $this->rsaMock = Mockery::mock(RSA::class); - $this->signerMock = Mockery::mock(JWT\Signer\Rsa\Sha256::class); $this->jwtMock = Mockery::mock(JWT\Token::class); - $this->jwtVerifier = new JwtVerifier($this->appleApiClient, $this->rsaMock, $this->signerMock); + $this->jwtVerifier = new JwtVerifier( + $this->appleApiClient, + $this->validatorMock, + $this->rsaMock, + new JWT\Signer\Rsa\Sha256() + ); } public function testIfVerifyThrowsKeysFetchingFailedExceptionWhenFailedToFetchJsonWebKeySet(): void @@ -71,9 +75,9 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenKidH ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('getHeader') + $this->jwtMock->shouldReceive('headers') ->once() - ->with('kid') + ->withNoArgs() ->andThrow(new OutOfBoundsException('`kid` header is missing')); $this->expectException(Exception\InvalidCryptographicAlgorithmException::class); @@ -88,10 +92,10 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAlgo ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('getHeader') + $this->jwtMock->shouldReceive('headers') ->once() - ->with('kid') - ->andReturn('foo'); + ->withNoArgs() + ->andReturn(new JWT\Token\DataSet(['kid' => 'foo'], '')); $this->expectException(Exception\InvalidCryptographicAlgorithmException::class); $this->expectExceptionMessage( @@ -107,70 +111,16 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAuth ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('getHeader') + $this->jwtMock->shouldReceive('headers') ->once() - ->with('kid') - ->andReturn('86D88Kf'); + ->withNoArgs() + ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->expectException(Exception\InvalidCryptographicAlgorithmException::class); $this->expectExceptionMessage('Unsupported cryptographic algorithm passed `86D88Kf'); $this->jwtVerifier->verify($this->jwtMock); } - public function testIfVerifyThrowsNotSignedTokenExceptionWhenTokenIsMissingSignature(): void - { - $this->appleApiClient->shouldReceive('getAuthKeys') - ->once() - ->withNoArgs() - ->andReturn( - new Api\Response\JsonWebKeySetCollection( - [ - '86D88Kf' => new Api\Response\JsonWebKeySet( - 'RSA', - '86D88Kf', - 'sig', - 'RS256', - 'iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ', - 'AQAB' - ), - ] - ) - ); - - $this->jwtMock->shouldReceive('getHeader') - ->once() - ->with('kid') - ->andReturn('86D88Kf'); - - $this->rsaMock->shouldReceive('loadKey') - ->once() - ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); - - return true; - } - ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - - $this->jwtMock->shouldReceive('verify') - ->once() - ->with($this->signerMock, 'publicKey') - ->andThrow(new BadMethodCallException('This token is not signed')); - - $this->expectException(Exception\NotSignedTokenException::class); - $this->expectExceptionMessage('This token is not signed'); - $this->jwtVerifier->verify($this->jwtMock); - } - public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void { $this->appleApiClient->shouldReceive('getAuthKeys') @@ -191,10 +141,10 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void ) ); - $this->jwtMock->shouldReceive('getHeader') + $this->jwtMock->shouldReceive('headers') ->once() - ->with('kid') - ->andReturn('86D88Kf'); + ->withNoArgs() + ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->rsaMock->shouldReceive('loadKey') ->once() @@ -215,9 +165,9 @@ function (array $argument) { ->once() ->andReturn('publicKey'); - $this->jwtMock->shouldReceive('verify') + $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->signerMock, 'publicKey') + ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) ->andReturn(true); self::assertTrue($this->jwtVerifier->verify($this->jwtMock)); @@ -243,10 +193,10 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrectMalformed(): void ) ); - $this->jwtMock->shouldReceive('getHeader') + $this->jwtMock->shouldReceive('headers') ->once() - ->with('kid') - ->andReturn('86D88Kf'); + ->withNoArgs() + ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->rsaMock->shouldReceive('loadKey') ->once() @@ -267,9 +217,9 @@ function (array $argument) { ->once() ->andReturn('publicKey'); - $this->jwtMock->shouldReceive('verify') + $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->signerMock, 'publicKey') + ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) ->andReturn(false); self::assertFalse($this->jwtVerifier->verify($this->jwtMock)); diff --git a/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php b/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php index c279df6..23e4103 100644 --- a/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php +++ b/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php @@ -7,6 +7,7 @@ use Azimo\Apple\Auth\Jwt; use Azimo\Apple\Auth\Service\AppleJwtFetchingService; use Azimo\Apple\Auth\Struct\JwtPayload; +use DateTimeImmutable; use Lcobucci\JWT\Token; use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -57,7 +58,7 @@ protected function setUp(): void public function testIfGetJwtPayloadThrowsVerificationFailedExceptionWhenVerificationFails(): void { - $token = new Token(); + $token = Mockery::mock(Token::class); $this->parserMock->shouldReceive('parse') ->once() ->with('json.web.token') @@ -78,7 +79,7 @@ public function testIfGetJwtPayloadThrowsVerificationFailedExceptionWhenVerifica public function testIfGetJwtPayloadThrowsValidationFailedExceptionWhenTokenIsInvalid(): void { - $token = new Token(); + $token = Mockery::mock(Token::class); $this->parserMock->shouldReceive('parse') ->once() ->with('json.web.token') @@ -101,7 +102,8 @@ public function testIfGetJwtPayloadThrowsValidationFailedExceptionWhenTokenIsInv public function testIfGetJwtPayloadReturnsExpectedJwtPayloadWhenTokenIsVerifiedAndValid(): void { - $token = new Token(); + $currentDate = new DateTimeImmutable(); + $token = Mockery::mock(Token::class); $this->parserMock->shouldReceive('parse') ->once() ->with('json.web.token') @@ -119,9 +121,9 @@ public function testIfGetJwtPayloadReturnsExpectedJwtPayloadWhenTokenIsVerifiedA $jwtPayload = new JwtPayload( 'https://appleid.apple.com', - 'com.acme.app', - 1591622611, - 1591622011, + ['com.acme.app'], + $currentDate, + $currentDate, 'foo.bar.baz', 'qGzMhtsfTCom-bl1PJYLHk', 'foo@privaterelay.appleid.com', From bf355ddc5fb0d44f2d98bf5ce5cd2b5ed7cafa9f Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 12:54:05 +0200 Subject: [PATCH 03/14] upgraded `phpseclib/phpseclib` to version 3.0.9 --- composer.json | 2 +- src/Auth/Jwt/JwtVerifier.php | 37 ++++---------- .../E2e/Auth/AppleJwtFetchingServiceTest.php | 4 +- tests/Unit/Auth/Jwt/JwtVerifierTest.php | 48 +------------------ 4 files changed, 11 insertions(+), 80 deletions(-) diff --git a/composer.json b/composer.json index ce1e23d..3151dfb 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "php": "^7.1", "ext-mbstring": "*", "ext-openssl": "*", - "phpseclib/phpseclib": "^2.0", + "phpseclib/phpseclib": "^3.0", "lcobucci/jwt": "4.0", "guzzlehttp/guzzle": "^6.0|^7.0" }, diff --git a/src/Auth/Jwt/JwtVerifier.php b/src/Auth/Jwt/JwtVerifier.php index fee074d..08330a9 100644 --- a/src/Auth/Jwt/JwtVerifier.php +++ b/src/Auth/Jwt/JwtVerifier.php @@ -8,8 +8,8 @@ use Azimo\Apple\Auth\Exception; use Lcobucci\JWT; use OutOfBoundsException; -use phpseclib\Crypt\RSA; -use phpseclib\Math\BigInteger; +use phpseclib3\Crypt\RSA; +use phpseclib3\Math\BigInteger; class JwtVerifier { @@ -18,11 +18,6 @@ class JwtVerifier */ private $client; - /** - * @var RSA - */ - private $rsa; - /** * @var JWT\Signer */ @@ -33,10 +28,9 @@ class JwtVerifier */ private $validator; - public function __construct(AppleApiClientInterface $client, JWT\Validator $validator, RSA $rsa, JWT\Signer $signer) + public function __construct(AppleApiClientInterface $client, JWT\Validator $validator, JWT\Signer $signer) { $this->client = $client; - $this->rsa = $rsa; $this->signer = $signer; $this->validator = $validator; } @@ -47,13 +41,11 @@ public function __construct(AppleApiClientInterface $client, JWT\Validator $vali */ public function verify(JWT\Token $jwt): bool { - $this->loadRsaKey($this->getAuthKey($jwt)); - return $this->validator->validate( $jwt, new JWT\Validation\Constraint\SignedWith( $this->signer, - JWT\Signer\Key\InMemory::plainText($this->rsa->getPublicKey()) + JWT\Signer\Key\InMemory::plainText($this->createPublicKey($this->getAuthKey($jwt))) ) ); } @@ -94,24 +86,11 @@ private function getAuthKey(JWT\Token $jwt): JsonWebKeySet return $authKey; } - private function loadRsaKey(JsonWebKeySet $authKey): void + private function createPublicKey(JsonWebKeySet $authKey): string { - /** - * Phpspeclib is parsing phpinfo(); output to determine OpenSSL Library and Header versions, - * basing on that set if MATH_BIGINTEGER_OPENSSL_ENABLED or MATH_BIGINTEGER_OPENSSL_DISABLED const. - * It crashes tests so it is possible that it might crash production, that is why constants are overwritten. - * - * @see vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php:273 - */ - if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); - } - - $this->rsa->loadKey( - [ - 'exponent' => new BigInteger(base64_decode(strtr($authKey->getExponent(), '-_', '+/')), 256), - 'modulus' => new BigInteger(base64_decode(strtr($authKey->getModulus(), '-_', '+/')), 256), - ] + return RSA\Formats\Keys\PKCS8::savePublicKey( + new BigInteger(base64_decode(strtr($authKey->getModulus(), '-_', '+/')), 256), + new BigInteger(base64_decode(strtr($authKey->getExponent(), '-_', '+/')), 256) ); } } diff --git a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php index c621a2d..09d3ed5 100644 --- a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php +++ b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php @@ -12,7 +12,6 @@ use Lcobucci\JWT\Validation\Constraint\PermittedFor; use Lcobucci\JWT\Validation\Validator; use Mockery\Adapter\Phpunit\MockeryTestCase; -use phpseclib\Crypt\RSA; final class AppleJwtFetchingServiceTest extends MockeryTestCase { @@ -39,7 +38,6 @@ public function setUp(): void new Api\Factory\ResponseFactory() ), new Validator(), - new RSA(), new Sha256() ), new Auth\Jwt\JwtValidator( @@ -56,7 +54,7 @@ public function setUp(): void public function testIfGetJwtPayloadReturnExpectedJwtPayload(): void { $jwtPayload = $this->appleJwtFetchingService->getJwtPayload( - 'eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MjQ2OTI0MTgsImlhdCI6MTYyNDYwNjAxOCwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJrZUVNV0dadGg5aDdDRUhIR1dtSVRRIiwiYXV0aF90aW1lIjoxNjI0NjA2MDE4LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.DQNhKgtNXGffuelanmMT_lnUPbIAVVEiDuj7NL-H4nusxZ5-lK5UBgWhCj79PX1NUxQKj2bOqcb2R2oE2POxIrkpm1jPSt5QXaBeBmwdKx6NtXss2BOq0TL8Jlp7N5UW2TSC2Dk3Cu8-WC-gQf9jgtnzsEkpJFO1G6XrG6ZWCLATDcinN2XRHKzbmxiwNdykUhi1EzH4ug5e0XWchdY5h8QeYqmP0K7SqXZZPxxmcWZC_9g02H58tgChrsYzDezOQxwrf08w7jDXKbaqlpThfh9FMNsMIGaFekhSneOth5_TSc-CPqSHdq3ORRqbiERWcqi0FGJmPnN8-oBVBkcEQA' + 'eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MjQ3MDE3MzUsImlhdCI6MTYyNDYxNTMzNSwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJzQWhpVmFTYXlKNlRSVFdoWFMxdGFBIiwiYXV0aF90aW1lIjoxNjI0NjE1MzM1LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.osvYd0hNosZKWD85-CJmyNXivFgWhrNCOdOpiB7VuYsRRMFn5cxZCFg8fBEiaekeVtHXMsilxoE7FUKfyZ14smi7QNk87qAcZ_ivF52x_l6hkR0YCANdbcIrJqFJQ2GwL1DHN4hE628qEZBf_dj5SdTcixHM-8X3ibWDt4irzBACiXqWvbaeRqFdhwJl-yG_of-9zjYg98-Hlk18MphxCgqmVhFXlOi_al4sVHdqZtUjMgGyqszmoIURgU9lOXXDGKZ3LyBU7vXJIZY4FjcXsOtJ4PO8N2LD_2EN2hXRdiUV_A86Dki_O9w17ZjYxlLwpsxRm_m3SzTVptgL0DL_7Q' ); self::assertInstanceOf(Auth\Struct\JwtPayload::class, $jwtPayload); diff --git a/tests/Unit/Auth/Jwt/JwtVerifierTest.php b/tests/Unit/Auth/Jwt/JwtVerifierTest.php index 095d7cc..c7e9a75 100644 --- a/tests/Unit/Auth/Jwt/JwtVerifierTest.php +++ b/tests/Unit/Auth/Jwt/JwtVerifierTest.php @@ -9,8 +9,7 @@ use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; use OutOfBoundsException; -use phpseclib\Crypt\RSA; -use phpseclib\Math\BigInteger; +use phpseclib3\Math\BigInteger; class JwtVerifierTest extends MockeryTestCase { @@ -19,11 +18,6 @@ class JwtVerifierTest extends MockeryTestCase */ private $appleApiClient; - /** - * @var RSA|Mockery\MockInterface - */ - private $rsaMock; - /** * @var JWT\Validator|Mockery\MockInterface */ @@ -45,13 +39,11 @@ protected function setUp(): void $this->validatorMock = Mockery::mock(JWT\Validator::class); $this->appleApiClient = Mockery::mock(Api\AppleApiClientInterface::class); - $this->rsaMock = Mockery::mock(RSA::class); $this->jwtMock = Mockery::mock(JWT\Token::class); $this->jwtVerifier = new JwtVerifier( $this->appleApiClient, $this->validatorMock, - $this->rsaMock, new JWT\Signer\Rsa\Sha256() ); } @@ -146,25 +138,6 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); - $this->rsaMock->shouldReceive('loadKey') - ->once() - ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); - - return true; - } - ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - $this->validatorMock->shouldReceive('validate') ->once() ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) @@ -198,25 +171,6 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrectMalformed(): void ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); - $this->rsaMock->shouldReceive('loadKey') - ->once() - ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); - - return true; - } - ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - $this->validatorMock->shouldReceive('validate') ->once() ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) From bfab29b6adeb7ba2b524d651c884ade015b0d54e Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 13:06:55 +0200 Subject: [PATCH 04/14] PHP 8.0 support --- .github/workflows/php.yml | 2 +- composer.json | 2 +- src/Api/AppleApiClient.php | 10 +-- src/Api/Response/JsonWebKeySet.php | 24 ++---- src/Api/Response/JsonWebKeySetCollection.php | 7 +- src/Auth/Jwt/JwtParser.php | 5 +- src/Auth/Jwt/JwtValidator.php | 10 +-- src/Auth/Jwt/JwtVerifier.php | 15 +--- src/Auth/Service/AppleJwtFetchingService.php | 20 +---- src/Auth/Struct/JwtPayload.php | 77 ++++++------------- .../E2e/Auth/AppleJwtFetchingServiceTest.php | 5 +- tests/Unit/Api/AppleApiClientTest.php | 19 ++--- .../Unit/Api/Factory/ResponseFactoryTest.php | 5 +- .../Factory/AppleJwtStructFactoryTest.php | 5 +- tests/Unit/Auth/Jwt/JwtParserTest.php | 12 +-- tests/Unit/Auth/Jwt/JwtValidatorTest.php | 24 +----- tests/Unit/Auth/Jwt/JwtVerifierTest.php | 55 +++++-------- .../Service/AppleJwtFetchingServiceTest.php | 33 +++----- 18 files changed, 88 insertions(+), 242 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 16c00e7..ddeee55 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -16,7 +16,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.3' + php-version: '8.0' tools: composer:v2 - name: Validate composer.json and composer.lock diff --git a/composer.json b/composer.json index 3151dfb..e9dc7a7 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ }, "minimum-stability": "stable", "require": { - "php": "^7.1", + "php": ">=7.4", "ext-mbstring": "*", "ext-openssl": "*", "phpseclib/phpseclib": "^3.0", diff --git a/src/Api/AppleApiClient.php b/src/Api/AppleApiClient.php index 42709f1..537b2a5 100644 --- a/src/Api/AppleApiClient.php +++ b/src/Api/AppleApiClient.php @@ -10,15 +10,9 @@ final class AppleApiClient implements AppleApiClientInterface { - /** - * @var GuzzleHttp\ClientInterface - */ - private $httpClient; + private GuzzleHttp\ClientInterface $httpClient; - /** - * @var ResponseFactory - */ - private $responseFactory; + private ResponseFactory $responseFactory; public function __construct(GuzzleHttp\ClientInterface $httpClient, ResponseFactory $responseFactory) { diff --git a/src/Api/Response/JsonWebKeySet.php b/src/Api/Response/JsonWebKeySet.php index 7c73715..5e6c40a 100644 --- a/src/Api/Response/JsonWebKeySet.php +++ b/src/Api/Response/JsonWebKeySet.php @@ -6,45 +6,33 @@ class JsonWebKeySet { /** * The family of cryptographic algorithms used with the key. - * - * @var string */ - private $kty; + private string $kty; /** * The unique identifier for the key. - * - * @var string */ - private $kid; + private string $kid; /** * How the key was meant to be used; `sig` represents the signature. - * - * @var string */ - private $use; + private string $use; /** * The specific cryptographic algorithm used with the key. - * - * @var string */ - private $alg; + private string $alg; /** * The modulus for the RSA public key. - * - * @var string */ - private $modulus; + private string $modulus; /** * The exponent for the RSA public key. - * - * @var string */ - private $exponent; + private string $exponent; public function __construct(string $kty, string $kid, string $use, string $alg, string $modulus, string $exponent) { diff --git a/src/Api/Response/JsonWebKeySetCollection.php b/src/Api/Response/JsonWebKeySetCollection.php index 1117faa..3349693 100644 --- a/src/Api/Response/JsonWebKeySetCollection.php +++ b/src/Api/Response/JsonWebKeySetCollection.php @@ -10,18 +10,13 @@ class JsonWebKeySetCollection /** * @var JsonWebKeySet[] */ - private $authKeys; + private array $authKeys; public function __construct(array $authKeys) { $this->authKeys = $authKeys; } - public function getAuthKeys(): array - { - return $this->authKeys; - } - public function getByCryptographicAlgorithm(string $algorithm): ?JsonWebKeySet { if (!CryptographicAlgorithmEnum::isSupported($algorithm)) { diff --git a/src/Auth/Jwt/JwtParser.php b/src/Auth/Jwt/JwtParser.php index ea79328..a1331ad 100644 --- a/src/Auth/Jwt/JwtParser.php +++ b/src/Auth/Jwt/JwtParser.php @@ -9,10 +9,7 @@ class JwtParser { - /** - * @var JWT\Parser - */ - private $jwtParser; + private JWT\Parser $jwtParser; public function __construct(JWT\Parser $jwtParser) { diff --git a/src/Auth/Jwt/JwtValidator.php b/src/Auth/Jwt/JwtValidator.php index 153b95b..c6f37b3 100644 --- a/src/Auth/Jwt/JwtValidator.php +++ b/src/Auth/Jwt/JwtValidator.php @@ -7,15 +7,9 @@ class JwtValidator { - /** - * @var JWT\Validator - */ - private $validator; + private JWT\Validator $validator; - /** - * @var array - */ - private $constraints; + private array $constraints; public function __construct(JWT\Validator $validator, array $constraints) { diff --git a/src/Auth/Jwt/JwtVerifier.php b/src/Auth/Jwt/JwtVerifier.php index 08330a9..16c40b6 100644 --- a/src/Auth/Jwt/JwtVerifier.php +++ b/src/Auth/Jwt/JwtVerifier.php @@ -13,20 +13,11 @@ class JwtVerifier { - /** - * @var AppleApiClientInterface - */ - private $client; + private AppleApiClientInterface $client; - /** - * @var JWT\Signer - */ - private $signer; + private JWT\Signer $signer; - /** - * @var JWT\Validator - */ - private $validator; + private JWT\Validator $validator; public function __construct(AppleApiClientInterface $client, JWT\Validator $validator, JWT\Signer $signer) { diff --git a/src/Auth/Service/AppleJwtFetchingService.php b/src/Auth/Service/AppleJwtFetchingService.php index 638c7b2..c266b17 100644 --- a/src/Auth/Service/AppleJwtFetchingService.php +++ b/src/Auth/Service/AppleJwtFetchingService.php @@ -9,25 +9,13 @@ final class AppleJwtFetchingService implements AppleJwtFetchingServiceInterface { - /** - * @var Jwt\JwtParser - */ - private $parser; + private Jwt\JwtParser $parser; - /** - * @var Jwt\JwtVerifier - */ - private $verifier; + private Jwt\JwtVerifier $verifier; - /** - * @var Jwt\JwtValidator - */ - private $validator; + private Jwt\JwtValidator $validator; - /** - * @var AppleJwtStructFactory - */ - private $factory; + private AppleJwtStructFactory $factory; public function __construct( Jwt\JwtParser $parser, diff --git a/src/Auth/Struct/JwtPayload.php b/src/Auth/Struct/JwtPayload.php index 4ed3c70..9d84a84 100644 --- a/src/Auth/Struct/JwtPayload.php +++ b/src/Auth/Struct/JwtPayload.php @@ -6,60 +6,27 @@ final class JwtPayload { - /** - * @var string - */ - private $iss; - - /** - * @var array - */ - private $aud; - - /** - * @var DateTimeInterface - */ - private $exp; - - /** - * @var DateTimeInterface - */ - private $iat; - - /** - * @var string - */ - private $sub; - - /** - * @var string - */ - private $cHash; - - /** - * @var string - */ - private $email; - - /** - * @var bool - */ - private $emailVerified; - - /** - * @var bool - */ - private $isPrivateEmail; - - /** - * @var int - */ - private $authTime; - - /** - * @var bool - */ - private $nonceSupported; + private string $iss; + + private array $aud; + + private DateTimeInterface $exp; + + private DateTimeInterface $iat; + + private string $sub; + + private string $cHash; + + private string $email; + + private bool $emailVerified; + + private bool $isPrivateEmail; + + private int $authTime; + + private bool $nonceSupported; public function __construct( string $iss, @@ -102,7 +69,7 @@ public function getExp(): DateTimeInterface return $this->exp; } - public function getIat§(): DateTimeInterface + public function getIat(): DateTimeInterface { return $this->iat; } diff --git a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php index 09d3ed5..a2ab81d 100644 --- a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php +++ b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php @@ -15,10 +15,7 @@ final class AppleJwtFetchingServiceTest extends MockeryTestCase { - /** - * @var Auth\Service\AppleJwtFetchingService - */ - private $appleJwtFetchingService; + private Auth\Service\AppleJwtFetchingService $appleJwtFetchingService; public function setUp(): void { diff --git a/tests/Unit/Api/AppleApiClientTest.php b/tests/Unit/Api/AppleApiClientTest.php index deb344e..6f56556 100644 --- a/tests/Unit/Api/AppleApiClientTest.php +++ b/tests/Unit/Api/AppleApiClientTest.php @@ -11,20 +11,11 @@ final class AppleApiClientTest extends MockeryTestCase { - /** - * @var Api\AppleApiClient - */ - private $client; - - /** - * @var GuzzleHttp\Client|\Mockery\MockInterface - */ - private $httpClientMock; - - /** - * @var Api\Factory\ResponseFactory|\Mockery\MockInterface - */ - private $responseFactoryMock; + private Api\AppleApiClient $client; + + private GuzzleHttp\Client $httpClientMock; + + private Api\Factory\ResponseFactory $responseFactoryMock; protected function setUp(): void { diff --git a/tests/Unit/Api/Factory/ResponseFactoryTest.php b/tests/Unit/Api/Factory/ResponseFactoryTest.php index 20cbd82..38a3f01 100644 --- a/tests/Unit/Api/Factory/ResponseFactoryTest.php +++ b/tests/Unit/Api/Factory/ResponseFactoryTest.php @@ -10,10 +10,7 @@ final class ResponseFactoryTest extends MockeryTestCase { - /** - * @var ResponseFactory - */ - private $responseFactory; + private ResponseFactory $responseFactory; protected function setUp(): void { diff --git a/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php b/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php index cf27f12..66ccafd 100644 --- a/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php +++ b/tests/Unit/Auth/Factory/AppleJwtStructFactoryTest.php @@ -10,10 +10,7 @@ final class AppleJwtStructFactoryTest extends MockeryTestCase { - /** - * @var AppleJwtStructFactory - */ - private $appleJwtStructFactory; + private AppleJwtStructFactory $appleJwtStructFactory; protected function setUp(): void { diff --git a/tests/Unit/Auth/Jwt/JwtParserTest.php b/tests/Unit/Auth/Jwt/JwtParserTest.php index a8026d4..58b45b0 100644 --- a/tests/Unit/Auth/Jwt/JwtParserTest.php +++ b/tests/Unit/Auth/Jwt/JwtParserTest.php @@ -13,15 +13,9 @@ final class JwtParserTest extends MockeryTestCase { - /** - * @var Parser|Mockery\MockInterface - */ - private $jwtParserMock; - - /** - * @var JwtParser - */ - private $parser; + private Parser $jwtParserMock; + + private JwtParser $parser; protected function setUp(): void { diff --git a/tests/Unit/Auth/Jwt/JwtValidatorTest.php b/tests/Unit/Auth/Jwt/JwtValidatorTest.php index c90e0e1..67d7772 100644 --- a/tests/Unit/Auth/Jwt/JwtValidatorTest.php +++ b/tests/Unit/Auth/Jwt/JwtValidatorTest.php @@ -10,29 +10,13 @@ final class JwtValidatorTest extends MockeryTestCase { - /** - * @var JWT\Token|Mockery\MockInterface - */ - private $jwtMock; + private JWT\Token $jwtMock; - /** - * @var - */ - private $constraints; + private array $constraints; - /** - * @var JWT\Validator|Mockery\MockInterface - */ - private $validatorMock; + private JWT\Validator $validatorMock; - /** - * @var JwtValidator JwtValidator - */ - private $jwtValidator; - - /** - * @var JwtValidator JwtValidator - */ + private JwtValidator $jwtValidator; protected function setUp(): void { diff --git a/tests/Unit/Auth/Jwt/JwtVerifierTest.php b/tests/Unit/Auth/Jwt/JwtVerifierTest.php index c7e9a75..795e97e 100644 --- a/tests/Unit/Auth/Jwt/JwtVerifierTest.php +++ b/tests/Unit/Auth/Jwt/JwtVerifierTest.php @@ -9,29 +9,16 @@ use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; use OutOfBoundsException; -use phpseclib3\Math\BigInteger; class JwtVerifierTest extends MockeryTestCase { - /** - * @var APi\AppleApiClient|Mockery\MockInterface - */ - private $appleApiClient; - - /** - * @var JWT\Validator|Mockery\MockInterface - */ - private $jwtMock; - - /** - * @var JWT\Validator|Mockery\MockInterface - */ - private $validatorMock; - - /** - * @var JwtVerifier - */ - private $jwtVerifier; + private APi\AppleApiClientInterface $appleApiClient; + + private JWT\Token $jwtTokenMock; + + private JWT\Validator $validatorMock; + + private JwtVerifier $jwtVerifier; protected function setUp(): void { @@ -39,7 +26,7 @@ protected function setUp(): void $this->validatorMock = Mockery::mock(JWT\Validator::class); $this->appleApiClient = Mockery::mock(Api\AppleApiClientInterface::class); - $this->jwtMock = Mockery::mock(JWT\Token::class); + $this->jwtTokenMock = Mockery::mock(JWT\Token::class); $this->jwtVerifier = new JwtVerifier( $this->appleApiClient, @@ -57,7 +44,7 @@ public function testIfVerifyThrowsKeysFetchingFailedExceptionWhenFailedToFetchJs $this->expectException(Exception\KeysFetchingFailedException::class); $this->expectExceptionMessage('Connection error'); - $this->jwtVerifier->verify($this->jwtMock); + $this->jwtVerifier->verify($this->jwtTokenMock); } public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenKidHeaderNotExist(): void @@ -67,14 +54,14 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenKidH ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('headers') + $this->jwtTokenMock->shouldReceive('headers') ->once() ->withNoArgs() ->andThrow(new OutOfBoundsException('`kid` header is missing')); $this->expectException(Exception\InvalidCryptographicAlgorithmException::class); $this->expectExceptionMessage('`kid` header is missing'); - $this->jwtVerifier->verify($this->jwtMock); + $this->jwtVerifier->verify($this->jwtTokenMock); } public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAlgorithmIsNotSupported(): void @@ -84,7 +71,7 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAlgo ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('headers') + $this->jwtTokenMock->shouldReceive('headers') ->once() ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => 'foo'], '')); @@ -93,7 +80,7 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAlgo $this->expectExceptionMessage( 'Cryptographic algorithm `foo` is not supported. Supported algorithms: `86D88Kf,eXaunmL,YuyXoY`' ); - $this->jwtVerifier->verify($this->jwtMock); + $this->jwtVerifier->verify($this->jwtTokenMock); } public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAuthKeyNotExistForGivenAlgorithm(): void @@ -103,14 +90,14 @@ public function testIfVerifyThrowsInvalidCryptographicAlgorithmExceptionWhenAuth ->withNoArgs() ->andReturn(new Api\Response\JsonWebKeySetCollection([])); - $this->jwtMock->shouldReceive('headers') + $this->jwtTokenMock->shouldReceive('headers') ->once() ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->expectException(Exception\InvalidCryptographicAlgorithmException::class); $this->expectExceptionMessage('Unsupported cryptographic algorithm passed `86D88Kf'); - $this->jwtVerifier->verify($this->jwtMock); + $this->jwtVerifier->verify($this->jwtTokenMock); } public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void @@ -133,17 +120,17 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void ) ); - $this->jwtMock->shouldReceive('headers') + $this->jwtTokenMock->shouldReceive('headers') ->once() ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) + ->with($this->jwtTokenMock, JWT\Validation\Constraint\SignedWith::class) ->andReturn(true); - self::assertTrue($this->jwtVerifier->verify($this->jwtMock)); + self::assertTrue($this->jwtVerifier->verify($this->jwtTokenMock)); } public function testIfVerifyReturnsTrueWhenTokenIsCorrectMalformed(): void @@ -166,16 +153,16 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrectMalformed(): void ) ); - $this->jwtMock->shouldReceive('headers') + $this->jwtTokenMock->shouldReceive('headers') ->once() ->withNoArgs() ->andReturn(new JWT\Token\DataSet(['kid' => '86D88Kf'], '')); $this->validatorMock->shouldReceive('validate') ->once() - ->with($this->jwtMock, JWT\Validation\Constraint\SignedWith::class) + ->with($this->jwtTokenMock, JWT\Validation\Constraint\SignedWith::class) ->andReturn(false); - self::assertFalse($this->jwtVerifier->verify($this->jwtMock)); + self::assertFalse($this->jwtVerifier->verify($this->jwtTokenMock)); } } diff --git a/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php b/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php index 23e4103..8a92a16 100644 --- a/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php +++ b/tests/Unit/Auth/Service/AppleJwtFetchingServiceTest.php @@ -14,30 +14,15 @@ class AppleJwtFetchingServiceTest extends MockeryTestCase { - /** - * @var Jwt\JwtParser|Mockery\MockInterface - */ - private $parserMock; - - /** - * @var Jwt\JwtVerifier|Mockery\MockInterface - */ - private $verifierMock; - - /** - * @var Jwt\JwtValidator|Mockery\MockInterface - */ - private $validatorMock; - - /** - * @var AppleJwtStructFactory|Mockery\MockInterface - */ - private $factoryMock; - - /** - * @var AppleJwtFetchingService - */ - private $jwtService; + private Jwt\JwtParser $parserMock; + + private Jwt\JwtVerifier $verifierMock; + + private Jwt\JwtValidator $validatorMock; + + private AppleJwtStructFactory $factoryMock; + + private AppleJwtFetchingService $jwtService; protected function setUp(): void { From bd66acb9ec72584679eb04f06b30059ffa69e23f Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 13:08:25 +0200 Subject: [PATCH 05/14] Bumped min php version to 7.4 --- .github/workflows/php.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 16c00e7..6cd1b7a 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -16,7 +16,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.3' + php-version: '7.4' tools: composer:v2 - name: Validate composer.json and composer.lock diff --git a/composer.json b/composer.json index 3151dfb..e9dc7a7 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ }, "minimum-stability": "stable", "require": { - "php": "^7.1", + "php": ">=7.4", "ext-mbstring": "*", "ext-openssl": "*", "phpseclib/phpseclib": "^3.0", From f20b26482f84776ec2c6b7a2975fd50e65514fb7 Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 13:14:01 +0200 Subject: [PATCH 06/14] Updated readme --- README.md | 86 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d1ce548..f41a59e 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,74 @@ # Sign-in with Apple SDK + [![Latest Version](https://img.shields.io/github/v/release/AzimoLabs/apple-sign-in-php-sdk.svg?style=flat-square)](https://github.com/AzimoLabs/apple-sign-in-php-sdk/releases) [![Build Status](https://img.shields.io/github/workflow/status/AzimoLabs/apple-sign-in-php-sdk/CI?label=ci%20build&style=flat-square)](https://github.com/AzimoLabs/apple-sign-in-php-sdk/actions?query=workflow%3ACI) ## Installation + Recommended and easiest way to installing library is through [Composer](https://getcomposer.org/). - + `composer require azimolabs/apple-sign-in-php-sdk` ## Requirements -* PHP 7.1+ + +* PHP 7.4+ * OpenSSL Extension +## PHP support +// todo + ## How it works -This description assumes that you already have generated [identityToken](https://developer.apple.com/documentation/authenticationservices/asauthorizationsinglesignoncredential/3153080-identitytoken). -Remember that token is valid ONLY for 10 minutes. -The first step to verify the identity token is to generate a public key. To generate public key `exponent` and `modulus` values are required. -Both information are exposed in [Apple API endpoint](https://appleid.apple.com/auth/keys). Those values differ depending on the algorithm. +This description assumes that you already have +generated [identityToken](https://developer.apple.com/documentation/authenticationservices/asauthorizationsinglesignoncredential/3153080-identitytoken) +. Remember that token is valid ONLY for 10 minutes. + +The first step to verify the identity token is to generate a public key. To generate public key `exponent` and `modulus` +values are required. Both information are exposed in [Apple API endpoint](https://appleid.apple.com/auth/keys). Those +values differ depending on the algorithm. -The second step is verification if provided `identityToken` is valid against generated public key. If so we are sure that `identityToken` wasn't malformed. +The second step is verification if provided `identityToken` is valid against generated public key. If so we are sure +that `identityToken` wasn't malformed. -The third step is validation if token is not expired. Additionally it is worth to check `issuer` and `audience`, examples are shown below. +The third step is validation if token is not expired. Additionally it is worth to check `issuer` and `audience`, +examples are shown below. ## Basic usage + Once you have cloned repository, make sure that composer dependencies are installed running `composer install -o`. ```php -$validationData = new ValidationData(); -$validationData->setIssuer('https://appleid.apple.com'); -$validationData->setAudience('com.azimo'); - $appleJwtFetchingService = new Auth\Service\AppleJwtFetchingService( - new Auth\Jwt\JwtParser(new Parser()), - new Auth\Jwt\JwtVerifier( - new Api\AppleApiClient( - new GuzzleHttp\Client( + new Auth\Jwt\JwtParser(new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder())), + new Auth\Jwt\JwtVerifier( + new Api\AppleApiClient( + new GuzzleHttp\Client( + [ + 'base_uri' => 'https://appleid.apple.com', + 'timeout' => 5, + 'connect_timeout' => 5, + ] + ), + new Api\Factory\ResponseFactory() + ), + new \Lcobucci\JWT\Validation\Validator(), + new \Lcobucci\JWT\Signer\Rsa\Sha256() + ), + new Auth\Jwt\JwtValidator( + new \Lcobucci\JWT\Validation\Validator(), [ - 'base_uri' => 'https://appleid.apple.com', - 'timeout' => 5, - 'connect_timeout' => 5, + new \Lcobucci\JWT\Validation\Constraint\IssuedBy('https://appleid.apple.com'), + new \Lcobucci\JWT\Validation\Constraint\PermittedFor('com.c.azimo.stage'), ] ), - new Api\Factory\ResponseFactory() - ), - new RSA(), - new Sha256() - ), - new Auth\Jwt\JwtValidator($validationData), - new Auth\Factory\AppleJwtStructFactory() -); + new Auth\Factory\AppleJwtStructFactory() + ); $appleJwtFetchingService->getJwtPayload('your.identity.token'); ``` -If you don't want to copy-paste above code you can paste freshly generated `identityToken` in `tests/E2e/Auth/AppleJwtFetchingServiceTest.php:53` +If you don't want to copy-paste above code you can paste freshly generated `identityToken` +in `tests/E2e/Auth/AppleJwtFetchingServiceTest.php:53` and run tests with simple command `php vendor/bin/phpunit tests/E2e`. ```shell script @@ -70,17 +85,24 @@ OK (1 test, 1 assertion) ``` ## Todo + It is welcome to open a pull request with a fix of any issue: -- [ ] Upgrade `phpseclib/phpseclib` to version `3.0.7` -- [ ] Upgrade `lcobucci/jwt` to version `4.x`. Reported in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) -- [x] Make library compatible with PHP `7.4.3`. Reported in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) +- [x] Upgrade `phpseclib/phpseclib` to version `3.0.7` +- [x] Upgrade `lcobucci/jwt` to version `4.x`. Reported + in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) +- [x] Make library compatible with PHP `7.4.3`. Reported + in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) - [ ] Make library compatible with PHP `8.0.0` ## Miscellaneous + * [JSON web token](https://jwt.io/) * [Sign in with Apple overwiew](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple) * [How backend token verification works](https://sarunw.com/posts/sign-in-with-apple-3/) # Towards financial services available to all -We’re working throughout the company to create faster, cheaper, and more available financial services all over the world, and here are some of the techniques that we’re utilizing. There’s still a long way ahead of us, and if you’d like to be part of that journey, check out our [careers page](https://bit.ly/3vajnu6). + +We’re working throughout the company to create faster, cheaper, and more available financial services all over the +world, and here are some of the techniques that we’re utilizing. There’s still a long way ahead of us, and if you’d like +to be part of that journey, check out our [careers page](https://bit.ly/3vajnu6). From c335f42fc8a3cec86ba93ed5c297faa0bf8f35af Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 13:32:57 +0200 Subject: [PATCH 07/14] upgraded `phpseclib/phpseclib` to version 3.0.9 --- composer.json | 2 +- src/Auth/Jwt/JwtVerifier.php | 37 ++------- .../E2e/Auth/AppleJwtFetchingServiceTest.php | 4 +- tests/Unit/Auth/Jwt/JwtVerifierTest.php | 76 +++++-------------- 4 files changed, 29 insertions(+), 90 deletions(-) diff --git a/composer.json b/composer.json index 4496ab2..18ad1b9 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "php": "^7.1", "ext-mbstring": "*", "ext-openssl": "*", - "phpseclib/phpseclib": "^2.0", + "phpseclib/phpseclib": "^3.0", "lcobucci/jwt": "3.3.3", "guzzlehttp/guzzle": "^6.0|^7.0" }, diff --git a/src/Auth/Jwt/JwtVerifier.php b/src/Auth/Jwt/JwtVerifier.php index 31cf840..b87878f 100644 --- a/src/Auth/Jwt/JwtVerifier.php +++ b/src/Auth/Jwt/JwtVerifier.php @@ -9,8 +9,8 @@ use BadMethodCallException; use Lcobucci\JWT; use OutOfBoundsException; -use phpseclib\Crypt\RSA; -use phpseclib\Math\BigInteger; +use phpseclib3\Crypt\RSA; +use phpseclib3\Math\BigInteger; class JwtVerifier { @@ -19,20 +19,14 @@ class JwtVerifier */ private $client; - /** - * @var RSA - */ - private $rsa; - /** * @var JWT\Signer */ private $signer; - public function __construct(AppleApiClient $client, RSA $rsa, JWT\Signer $signer) + public function __construct(AppleApiClient $client, JWT\Signer $signer) { $this->client = $client; - $this->rsa = $rsa; $this->signer = $signer; } @@ -43,10 +37,8 @@ public function __construct(AppleApiClient $client, RSA $rsa, JWT\Signer $signer */ public function verify(JWT\Token $jwt): bool { - $this->loadRsaKey($this->getAuthKey($jwt)); - try { - return $jwt->verify($this->signer, $this->rsa->getPublicKey()); + return $jwt->verify($this->signer, $this->createPublicKey($this->getAuthKey($jwt))); } catch (BadMethodCallException $exception) { throw new Exception\NotSignedTokenException($exception->getMessage(), $exception->getCode(), $exception); } @@ -88,24 +80,11 @@ private function getAuthKey(JWT\Token $jwt): JsonWebKeySet return $authKey; } - private function loadRsaKey(JsonWebKeySet $authKey): void + private function createPublicKey(JsonWebKeySet $authKey): string { - /** - * Phpspeclib is parsing phpinfo(); output to determine OpenSSL Library and Header versions, - * basing on that set if MATH_BIGINTEGER_OPENSSL_ENABLED or MATH_BIGINTEGER_OPENSSL_DISABLED const. - * It crashes tests so it is possible that it might crash production, that is why constants are overwritten. - * - * @see vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php:273 - */ - if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); - } - - $this->rsa->loadKey( - [ - 'exponent' => new BigInteger(base64_decode(strtr($authKey->getExponent(), '-_', '+/')), 256), - 'modulus' => new BigInteger(base64_decode(strtr($authKey->getModulus(), '-_', '+/')), 256), - ] + return RSA\Formats\Keys\PKCS8::savePublicKey( + new BigInteger(base64_decode(strtr($authKey->getModulus(), '-_', '+/')), 256), + new BigInteger(base64_decode(strtr($authKey->getExponent(), '-_', '+/')), 256) ); } } diff --git a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php index c9e1ed0..3b3a667 100644 --- a/tests/E2e/Auth/AppleJwtFetchingServiceTest.php +++ b/tests/E2e/Auth/AppleJwtFetchingServiceTest.php @@ -9,7 +9,6 @@ use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\ValidationData; use Mockery\Adapter\Phpunit\MockeryTestCase; -use phpseclib\Crypt\RSA; class AppleJwtFetchingServiceTest extends MockeryTestCase { @@ -39,7 +38,6 @@ public function setUp(): void ), new Api\Factory\ResponseFactory() ), - new RSA(), new Sha256() ), new Auth\Jwt\JwtValidator($validationData), @@ -50,7 +48,7 @@ public function setUp(): void public function testIfGetJwtPayloadReturnExpectedJwtPayload(): void { $jwtPayload = $this->appleJwtFetchingService->getJwtPayload( - 'eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MTMyMTIzNjIsImlhdCI6MTYxMzEyNTk2Miwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJ4SGpPV24zblpUa3JTS1dRSGRRZmFBIiwiYXV0aF90aW1lIjoxNjEzMTI1OTYyLCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.YShVEmo-QGDnMxU_M9wkwOFcqC5vqvMvXDDlZvQ1VO-WA74_CYOBMbdMKvvTWWGgpvnykNVduvixuFkv_3vpRo2llydwllmVJtMxshTx-kIDmBnInP03lP2jdaDSonDmm0UiXtGEmOqqeFiT_sgUn5o0jfUUreNrXMBM9eLpzEDcjyMW_u3qBhds2SQlsJew6Hd9w16lMTngrJYrMq2H6gogWaCqoXdXexJGdQYfBiX2J14XEgGAyW_7ZupFKT0YCb_OwBQubocsdKbRiw7KlHZVH4vcCaz6e5as9Z-g9V8o4eOFMhuYaugmuGdpBruulyOgDgdPYmR3JCMdPeLFdA' + 'eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmMuYXppbW8uc3RhZ2UiLCJleHAiOjE2MjQ3MDE3MzUsImlhdCI6MTYyNDYxNTMzNSwic3ViIjoiMDAwNTYwLjE4MDM2YjI3MmI5MjRkYTg5ZWY3N2RjNDYyNDhkODRhLjA3MjEiLCJjX2hhc2giOiJzQWhpVmFTYXlKNlRSVFdoWFMxdGFBIiwiYXV0aF90aW1lIjoxNjI0NjE1MzM1LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.osvYd0hNosZKWD85-CJmyNXivFgWhrNCOdOpiB7VuYsRRMFn5cxZCFg8fBEiaekeVtHXMsilxoE7FUKfyZ14smi7QNk87qAcZ_ivF52x_l6hkR0YCANdbcIrJqFJQ2GwL1DHN4hE628qEZBf_dj5SdTcixHM-8X3ibWDt4irzBACiXqWvbaeRqFdhwJl-yG_of-9zjYg98-Hlk18MphxCgqmVhFXlOi_al4sVHdqZtUjMgGyqszmoIURgU9lOXXDGKZ3LyBU7vXJIZY4FjcXsOtJ4PO8N2LD_2EN2hXRdiUV_A86Dki_O9w17ZjYxlLwpsxRm_m3SzTVptgL0DL_7Q' ); self::assertInstanceOf(Auth\Struct\JwtPayload::class, $jwtPayload); diff --git a/tests/Unit/Auth/Jwt/JwtVerifierTest.php b/tests/Unit/Auth/Jwt/JwtVerifierTest.php index 4647b24..32d45da 100644 --- a/tests/Unit/Auth/Jwt/JwtVerifierTest.php +++ b/tests/Unit/Auth/Jwt/JwtVerifierTest.php @@ -10,8 +10,6 @@ use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; use OutOfBoundsException; -use phpseclib\Crypt\RSA; -use phpseclib\Math\BigInteger; class JwtVerifierTest extends MockeryTestCase { @@ -20,11 +18,6 @@ class JwtVerifierTest extends MockeryTestCase */ private $appleApiClient; - /** - * @var RSA|Mockery\MockInterface - */ - private $rsaMock; - /** * @var JWT\Signer\Rsa\Sha256|Mockery\MockInterface */ @@ -45,11 +38,10 @@ protected function setUp(): void parent::setUp(); $this->appleApiClient = Mockery::mock(Api\AppleApiClient::class); - $this->rsaMock = Mockery::mock(RSA::class); $this->signerMock = Mockery::mock(JWT\Signer\Rsa\Sha256::class); $this->jwtMock = Mockery::mock(JWT\Token::class); - $this->jwtVerifier = new JwtVerifier($this->appleApiClient, $this->rsaMock, $this->signerMock); + $this->jwtVerifier = new JwtVerifier($this->appleApiClient, $this->signerMock); } public function testIfVerifyThrowsKeysFetchingFailedExceptionWhenFailedToFetchJsonWebKeySet(): void @@ -142,28 +134,18 @@ public function testIfVerifyThrowsNotSignedTokenExceptionWhenTokenIsMissingSigna ->with('kid') ->andReturn('86D88Kf'); - $this->rsaMock->shouldReceive('loadKey') + $this->jwtMock->shouldReceive('verify') ->once() ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); + $this->signerMock, + \Mockery::on( + function (string $certificate) { + self::assertStringContainsString('BEGIN PUBLIC KEY', $certificate); return true; } ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - - $this->jwtMock->shouldReceive('verify') - ->once() - ->with($this->signerMock, 'publicKey') + ) ->andThrow(new BadMethodCallException('This token is not signed')); $this->expectException(Exception\NotSignedTokenException::class); @@ -196,28 +178,18 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrect(): void ->with('kid') ->andReturn('86D88Kf'); - $this->rsaMock->shouldReceive('loadKey') + $this->jwtMock->shouldReceive('verify') ->once() ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); + $this->signerMock, + \Mockery::on( + function (string $certificate) { + self::assertStringContainsString('BEGIN PUBLIC KEY', $certificate); return true; } ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - - $this->jwtMock->shouldReceive('verify') - ->once() - ->with($this->signerMock, 'publicKey') + ) ->andReturn(true); self::assertTrue($this->jwtVerifier->verify($this->jwtMock)); @@ -248,28 +220,18 @@ public function testIfVerifyReturnsTrueWhenTokenIsCorrectMalformed(): void ->with('kid') ->andReturn('86D88Kf'); - $this->rsaMock->shouldReceive('loadKey') + $this->jwtMock->shouldReceive('verify') ->once() ->with( - Mockery::on( - function (array $argument) { - $this->assertArrayHasKey('exponent', $argument); - $this->assertArrayHasKey('modulus', $argument); - $this->assertInstanceOf(BigInteger::class, $argument['exponent']); - $this->assertInstanceOf(BigInteger::class, $argument['modulus']); + $this->signerMock, + \Mockery::on( + function (string $certificate) { + self::assertStringContainsString('BEGIN PUBLIC KEY', $certificate); return true; } ) - ); - $this->rsaMock->shouldReceive('getPublicKey') - ->withNoArgs() - ->once() - ->andReturn('publicKey'); - - $this->jwtMock->shouldReceive('verify') - ->once() - ->with($this->signerMock, 'publicKey') + ) ->andReturn(false); self::assertFalse($this->jwtVerifier->verify($this->jwtMock)); From 1f1619f12f6ca553a7b033f9141061d48a3b26bc Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 13:44:23 +0200 Subject: [PATCH 08/14] updated readme --- README.md | 74 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index d1ce548..d05601e 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,37 @@ # Sign-in with Apple SDK + [![Latest Version](https://img.shields.io/github/v/release/AzimoLabs/apple-sign-in-php-sdk.svg?style=flat-square)](https://github.com/AzimoLabs/apple-sign-in-php-sdk/releases) [![Build Status](https://img.shields.io/github/workflow/status/AzimoLabs/apple-sign-in-php-sdk/CI?label=ci%20build&style=flat-square)](https://github.com/AzimoLabs/apple-sign-in-php-sdk/actions?query=workflow%3ACI) ## Installation + Recommended and easiest way to installing library is through [Composer](https://getcomposer.org/). - + `composer require azimolabs/apple-sign-in-php-sdk` ## Requirements + * PHP 7.1+ * OpenSSL Extension ## How it works -This description assumes that you already have generated [identityToken](https://developer.apple.com/documentation/authenticationservices/asauthorizationsinglesignoncredential/3153080-identitytoken). -Remember that token is valid ONLY for 10 minutes. -The first step to verify the identity token is to generate a public key. To generate public key `exponent` and `modulus` values are required. -Both information are exposed in [Apple API endpoint](https://appleid.apple.com/auth/keys). Those values differ depending on the algorithm. +This description assumes that you already have +generated [identityToken](https://developer.apple.com/documentation/authenticationservices/asauthorizationsinglesignoncredential/3153080-identitytoken) +. Remember that token is valid ONLY for 10 minutes. + +The first step to verify the identity token is to generate a public key. To generate public key `exponent` and `modulus` +values are required. Both information are exposed in [Apple API endpoint](https://appleid.apple.com/auth/keys). Those +values differ depending on the algorithm. -The second step is verification if provided `identityToken` is valid against generated public key. If so we are sure that `identityToken` wasn't malformed. +The second step is verification if provided `identityToken` is valid against generated public key. If so we are sure +that `identityToken` wasn't malformed. -The third step is validation if token is not expired. Additionally it is worth to check `issuer` and `audience`, examples are shown below. +The third step is validation if token is not expired. Additionally it is worth to check `issuer` and `audience`, +examples are shown below. ## Basic usage + Once you have cloned repository, make sure that composer dependencies are installed running `composer install -o`. ```php @@ -31,29 +40,29 @@ $validationData->setIssuer('https://appleid.apple.com'); $validationData->setAudience('com.azimo'); $appleJwtFetchingService = new Auth\Service\AppleJwtFetchingService( - new Auth\Jwt\JwtParser(new Parser()), - new Auth\Jwt\JwtVerifier( - new Api\AppleApiClient( - new GuzzleHttp\Client( - [ - 'base_uri' => 'https://appleid.apple.com', - 'timeout' => 5, - 'connect_timeout' => 5, - ] + new Auth\Jwt\JwtParser(new Parser()), + new Auth\Jwt\JwtVerifier( + new Api\AppleApiClient( + new GuzzleHttp\Client( + [ + 'base_uri' => 'https://appleid.apple.com', + 'timeout' => 5, + 'connect_timeout' => 5, + ] + ), + new Api\Factory\ResponseFactory() + ), + new \Lcobucci\JWT\Signer\Rsa\Sha256() ), - new Api\Factory\ResponseFactory() - ), - new RSA(), - new Sha256() - ), - new Auth\Jwt\JwtValidator($validationData), - new Auth\Factory\AppleJwtStructFactory() -); + new Auth\Jwt\JwtValidator($validationData), + new Auth\Factory\AppleJwtStructFactory() + ); $appleJwtFetchingService->getJwtPayload('your.identity.token'); ``` -If you don't want to copy-paste above code you can paste freshly generated `identityToken` in `tests/E2e/Auth/AppleJwtFetchingServiceTest.php:53` +If you don't want to copy-paste above code you can paste freshly generated `identityToken` +in `tests/E2e/Auth/AppleJwtFetchingServiceTest.php:53` and run tests with simple command `php vendor/bin/phpunit tests/E2e`. ```shell script @@ -70,17 +79,24 @@ OK (1 test, 1 assertion) ``` ## Todo + It is welcome to open a pull request with a fix of any issue: -- [ ] Upgrade `phpseclib/phpseclib` to version `3.0.7` -- [ ] Upgrade `lcobucci/jwt` to version `4.x`. Reported in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) -- [x] Make library compatible with PHP `7.4.3`. Reported in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) +- [x] Upgrade `phpseclib/phpseclib` to version `3.0.7` +- [ ] Upgrade `lcobucci/jwt` to version `4.x`. Reported + in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) +- [x] Make library compatible with PHP `7.4.3`. Reported + in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) - [ ] Make library compatible with PHP `8.0.0` ## Miscellaneous + * [JSON web token](https://jwt.io/) * [Sign in with Apple overwiew](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple) * [How backend token verification works](https://sarunw.com/posts/sign-in-with-apple-3/) # Towards financial services available to all -We’re working throughout the company to create faster, cheaper, and more available financial services all over the world, and here are some of the techniques that we’re utilizing. There’s still a long way ahead of us, and if you’d like to be part of that journey, check out our [careers page](https://bit.ly/3vajnu6). + +We’re working throughout the company to create faster, cheaper, and more available financial services all over the +world, and here are some of the techniques that we’re utilizing. There’s still a long way ahead of us, and if you’d like +to be part of that journey, check out our [careers page](https://bit.ly/3vajnu6). From 100d1b79550ba18e870a51b7a8ec280207687f60 Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 14:07:48 +0200 Subject: [PATCH 09/14] updated readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d05601e..8a58bfc 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ Recommended and easiest way to installing library is through [Composer](https:// * PHP 7.1+ * OpenSSL Extension +## PHP support +|PHP version|Library version| +|---|---| +|`5.x`|`NOT SUPPORTED`| +| `> 7.0 <= 7.3`| `1.x.x` | + ## How it works This description assumes that you already have From b0171acb459873ebb780ebe667d4b0e4eb06f9b2 Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 14:25:12 +0200 Subject: [PATCH 10/14] updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 69b82ba..d39251a 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ $validationData = new ValidationData(); $validationData->setIssuer('https://appleid.apple.com'); $validationData->setAudience('com.azimo'); -$appleJwtFetchingService new Auth\Service\AppleJwtFetchingService( +$appleJwtFetchingService = new Auth\Service\AppleJwtFetchingService( new Auth\Jwt\JwtParser(new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder())), new Auth\Jwt\JwtVerifier( new Api\AppleApiClient( @@ -97,7 +97,7 @@ OK (1 test, 1 assertion) It is welcome to open a pull request with a fix of any issue: - [x] Upgrade `phpseclib/phpseclib` to version `3.0.7` -- [ ] Upgrade `lcobucci/jwt` to version `4.x`. Reported +- [x] Upgrade `lcobucci/jwt` to version `4.x`. Reported in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) - [x] Make library compatible with PHP `7.4.3`. Reported in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) From 2ef71383bd1b6a219131388ba38bfc4a49f6c74c Mon Sep 17 00:00:00 2001 From: Michal Baran Date: Fri, 25 Jun 2021 14:29:07 +0200 Subject: [PATCH 11/14] updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d39251a..3deacd1 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ Recommended and easiest way to installing library is through [Composer](https:// |PHP version|Library version| |---|---| |`5.x`|`NOT SUPPORTED`| -| `> 7.0 <= 7.3`| `1.x.x` | -| `> 7.4 < 8.0`| `2.x.x` | +| `> 7.0 <= 7.3`| `1.4.x` | +| `> 7.4 < 8.0`| `1.5.x` | ## How it works From b11ba04f805a019215954a498fd55aaaf8fef88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Fri, 25 Jun 2021 14:44:58 +0200 Subject: [PATCH 12/14] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e9dc7a7..b950629 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "ext-mbstring": "*", "ext-openssl": "*", "phpseclib/phpseclib": "^3.0", - "lcobucci/jwt": "4.0", + "lcobucci/jwt": "^4.0", "guzzlehttp/guzzle": "^6.0|^7.0" }, "require-dev": { From e816446e3f47f5570c2aaebdf8818354e290981c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Fri, 25 Jun 2021 14:58:24 +0200 Subject: [PATCH 13/14] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3deacd1..482e3ae 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Recommended and easiest way to installing library is through [Composer](https:// |`5.x`|`NOT SUPPORTED`| | `> 7.0 <= 7.3`| `1.4.x` | | `> 7.4 < 8.0`| `1.5.x` | +| `> 8.0 & ^7.4`| `2.0.x` | ## How it works From dc37aaaaa37582c0f4a7fabd40a04b80fa3b12f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Mon, 28 Jun 2021 16:42:59 +0200 Subject: [PATCH 14/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 482e3ae..df443dc 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ It is welcome to open a pull request with a fix of any issue: in: [Implicit conversion of keys from strings is deprecated. #2](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/2) - [x] Make library compatible with PHP `7.4.3`. Reported in [Uncaught JsonException: Malformed UTF-8 characters](https://github.com/AzimoLabs/apple-sign-in-php-sdk/issues/4) -- [ ] Make library compatible with PHP `8.0.0` +- [x] Make library compatible with PHP `8.0.0` ## Miscellaneous