From 915b38198d0a5c7afbcf0cec7483735f2a227e98 Mon Sep 17 00:00:00 2001 From: Al Date: Thu, 5 Dec 2024 17:24:40 +1300 Subject: [PATCH 1/7] Add Amazon Cognito --- README.md | 1 + docs/resource_owners/amazon_cognito.md | 31 ++++++++ .../AmazonCognitoResourceOwner.php | 63 ++++++++++++++++ .../AmazonCognitoResourceOwnerTest.php | 72 +++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 docs/resource_owners/amazon_cognito.md create mode 100644 src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php create mode 100644 tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php diff --git a/README.md b/README.md index 1f2527e43..05f25cab9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ file in this bundle. Read the documentation for version: This bundle contains support for 58 different providers: * 37signals, * Amazon, +* Amazon Cognito * Apple, * Asana, * Auth0, diff --git a/docs/resource_owners/amazon_cognito.md b/docs/resource_owners/amazon_cognito.md new file mode 100644 index 000000000..64f004128 --- /dev/null +++ b/docs/resource_owners/amazon_cognito.md @@ -0,0 +1,31 @@ +Step 2x: Setup Amazon Cognito +===================== +1. First you will need to creat a user pool on Amazon Cognito https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-next-steps.html. +2. Next, we need to create an app client for this user pool so that we can use Cognito’s OAuth 2.0 service. Make sure to take note of the `client_id` and `client_secret` as we will need them later. https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html +3. Add a callback URL `{HOST}/security/cognito/check`. +4. You will also need cognito `domain` and `region` (found in Amazon Cognito) + +Next configure a resource owner of type `amazon_cognito` with appropriate +`client_id`, `client_secret` and `scope`. Refer to the Amazon documentation +for the available scopes. + +``` yaml +# config/packages/hwi_oauth.yaml + +hwi_oauth: + resource_owners: + any_name: + type: amazon_cognito + client_id: + client_secret: + scope: "email openid" #needs to be enabled in cognito (profile, phone) + options: + region: + domain: +``` + +When you're done. Continue by configuring the security layer or go back to +setup more resource owners. + +- [Step 2: Configuring resource owners (Facebook, GitHub, Google, Windows Live and others](../2-configuring_resource_owners.md) +- [Step 3: Configuring the security layer](../3-configuring_the_security_layer.md). diff --git a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php new file mode 100644 index 000000000..ec364eeb9 --- /dev/null +++ b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; + +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @author Latysh + */ +final class AmazonCognitoResourceOwner extends GenericOAuth2ResourceOwner +{ + public const TYPE = 'amazon_cognito'; + + protected array $paths = [ + 'identifier' => 'sub', + 'firstname' => 'given_name', + 'lastname' => 'family_name', + 'email' => 'email', + 'nickname' => 'nickname', + 'realname' => 'name', + ]; + + protected function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + $resolver->setDefaults([ + 'authorization_url' => '{base_url}/oauth2/authorize', + 'access_token_url' => '{base_url}/oauth2/token', + 'revoke_token_url' => '{base_url}/oauth2/revoke', + 'infos_url' => '{base_url}/oauth2/userInfo', + 'use_commas_in_scope' => true, + ]); + + $resolver->setRequired([ + 'pool_id', + 'region', + 'domain', + ]); + + $normalizer = function (Options $options, $value) { + $baseUrl = sprintf('https://%s.auth.%s.amazoncognito.com', $options['domain'], $options['region']); + + return str_replace('{base_url}', $baseUrl, $value); + }; + + $resolver + ->setNormalizer('authorization_url', $normalizer) + ->setNormalizer('access_token_url', $normalizer) + ->setNormalizer('revoke_token_url', $normalizer) + ->setNormalizer('infos_url', $normalizer); + } +} diff --git a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php new file mode 100644 index 000000000..2b11cef7d --- /dev/null +++ b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; + +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AmazonCognitoResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; +use HWI\Bundle\OAuthBundle\Test\OAuth\ResourceOwner\GenericOAuth2ResourceOwnerTestCase; + +final class AmazonCognitoResourceOwnerTest extends GenericOAuth2ResourceOwnerTestCase +{ + protected string $resourceOwnerClass = AmazonCognitoResourceOwner::class; + protected string $userResponse = << 'user_id', + 'nickname' => 'name', + 'realname' => 'name', + 'email' => 'email', + ]; + public function testGetUserInformation(): void + { + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); + + /** + * @var AbstractUserResponse + */ + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); + + $this->assertEquals('111', $userResponse->getUserIdentifier()); + $this->assertEquals('baz@example.com', $userResponse->getEmail()); + $this->assertEquals('bar', $userResponse->getRealName()); + $this->assertNull($userResponse->getFirstName()); + $this->assertNull($userResponse->getProfilePicture()); + $this->assertEquals('token', $userResponse->getAccessToken()); + } + + public function testGetUserInformationFailure(): void + { + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'application/json; charset=utf-8', 401), + ] + ); + + $resourceOwner->getUserInformation($this->tokenData); + } +} From 8336d82e769b6abae2dada49f71e8760ed2c1004 Mon Sep 17 00:00:00 2001 From: Al Date: Thu, 5 Dec 2024 17:27:33 +1300 Subject: [PATCH 2/7] Add a link to docs --- docs/2-configuring_resource_owners.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/2-configuring_resource_owners.md b/docs/2-configuring_resource_owners.md index 3a4211a59..a6dbcf65d 100644 --- a/docs/2-configuring_resource_owners.md +++ b/docs/2-configuring_resource_owners.md @@ -33,6 +33,7 @@ hwi_oauth: - [37signals](resource_owners/37signals.md) - [Asana](resource_owners/asana.md) - [Amazon](resource_owners/amazon.md) +- [Amazon Cognito](resource_owners/amazon_cognito.md) - [Apple](resource_owners/apple.md) - [Auth0](resource_owners/auth0.md) - [Azure](resource_owners/azure.md) From d0f9d126f1162a379aa02c97636cffcd782d5605 Mon Sep 17 00:00:00 2001 From: Al Date: Thu, 5 Dec 2024 17:28:41 +1300 Subject: [PATCH 3/7] Remove not needed option --- src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php index ec364eeb9..46710315a 100644 --- a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php +++ b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php @@ -43,7 +43,6 @@ protected function configureOptions(OptionsResolver $resolver) ]); $resolver->setRequired([ - 'pool_id', 'region', 'domain', ]); From 42c56c042a46943722f3eb712c7f16226e6ef7e1 Mon Sep 17 00:00:00 2001 From: Al Date: Thu, 5 Dec 2024 17:33:24 +1300 Subject: [PATCH 4/7] Remove default not needed --- src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php index 46710315a..ffb291fdd 100644 --- a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php +++ b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php @@ -35,11 +35,10 @@ protected function configureOptions(OptionsResolver $resolver) parent::configureOptions($resolver); $resolver->setDefaults([ - 'authorization_url' => '{base_url}/oauth2/authorize', - 'access_token_url' => '{base_url}/oauth2/token', - 'revoke_token_url' => '{base_url}/oauth2/revoke', - 'infos_url' => '{base_url}/oauth2/userInfo', - 'use_commas_in_scope' => true, + 'authorization_url' => '{base_url}/oauth2/authorize', + 'access_token_url' => '{base_url}/oauth2/token', + 'revoke_token_url' => '{base_url}/oauth2/revoke', + 'infos_url' => '{base_url}/oauth2/userInfo' ]); $resolver->setRequired([ From f125fedc48317715e8d520a5c66ac55f3a9536d0 Mon Sep 17 00:00:00 2001 From: Al Date: Mon, 9 Dec 2024 09:18:22 +1300 Subject: [PATCH 5/7] Fix the test and PSR --- .../AmazonCognitoResourceOwner.php | 18 +++++++++--------- .../AmazonCognitoResourceOwnerTest.php | 6 +++++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php index ffb291fdd..bd964b35c 100644 --- a/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php +++ b/src/OAuth/ResourceOwner/AmazonCognitoResourceOwner.php @@ -23,11 +23,11 @@ final class AmazonCognitoResourceOwner extends GenericOAuth2ResourceOwner protected array $paths = [ 'identifier' => 'sub', - 'firstname' => 'given_name', - 'lastname' => 'family_name', - 'email' => 'email', - 'nickname' => 'nickname', - 'realname' => 'name', + 'firstname' => 'given_name', + 'lastname' => 'family_name', + 'email' => 'email', + 'nickname' => 'nickname', + 'realname' => 'name', ]; protected function configureOptions(OptionsResolver $resolver) @@ -36,9 +36,9 @@ protected function configureOptions(OptionsResolver $resolver) $resolver->setDefaults([ 'authorization_url' => '{base_url}/oauth2/authorize', - 'access_token_url' => '{base_url}/oauth2/token', - 'revoke_token_url' => '{base_url}/oauth2/revoke', - 'infos_url' => '{base_url}/oauth2/userInfo' + 'access_token_url' => '{base_url}/oauth2/token', + 'revoke_token_url' => '{base_url}/oauth2/revoke', + 'infos_url' => '{base_url}/oauth2/userInfo', ]); $resolver->setRequired([ @@ -47,7 +47,7 @@ protected function configureOptions(OptionsResolver $resolver) ]); $normalizer = function (Options $options, $value) { - $baseUrl = sprintf('https://%s.auth.%s.amazoncognito.com', $options['domain'], $options['region']); + $baseUrl = \sprintf('https://%s.auth.%s.amazoncognito.com', $options['domain'], $options['region']); return str_replace('{base_url}', $baseUrl, $value); }; diff --git a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php index 2b11cef7d..87fcf6265 100644 --- a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php +++ b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php @@ -32,10 +32,14 @@ final class AmazonCognitoResourceOwnerTest extends GenericOAuth2ResourceOwnerTes 'realname' => 'name', 'email' => 'email', ]; + public function testGetUserInformation(): void { $resourceOwner = $this->createResourceOwner( - [], + [ + 'domain' => 'test.com', + 'region' => 'us-east-1', + ], [], [ $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), From b00480d8bb66140235c20f5ba7eebd8cb6c3c8d6 Mon Sep 17 00:00:00 2001 From: Al Date: Mon, 9 Dec 2024 15:49:08 +1300 Subject: [PATCH 6/7] Fix tests --- .../ResourceOwner/AmazonCognitoResourceOwnerTest.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php index 87fcf6265..9981502c3 100644 --- a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php +++ b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php @@ -14,6 +14,7 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AmazonCognitoResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Test\OAuth\ResourceOwner\GenericOAuth2ResourceOwnerTestCase; +use Symfony\Component\Security\Core\Exception\AuthenticationException; final class AmazonCognitoResourceOwnerTest extends GenericOAuth2ResourceOwnerTestCase { @@ -40,7 +41,7 @@ public function testGetUserInformation(): void 'domain' => 'test.com', 'region' => 'us-east-1', ], - [], + $this->paths, [ $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), ] @@ -64,7 +65,10 @@ public function testGetUserInformationFailure(): void $this->expectException(AuthenticationException::class); $resourceOwner = $this->createResourceOwner( - [], + [ + 'domain' => 'test.com', + 'region' => 'us-east-1', + ], [], [ $this->createMockResponse('invalid', 'application/json; charset=utf-8', 401), From d9bc5bc4b2472cf9796e2a85a94f0d29a96d07cc Mon Sep 17 00:00:00 2001 From: Al Date: Mon, 9 Dec 2024 17:12:51 +1300 Subject: [PATCH 7/7] Tests fix 2 --- .../AmazonCognitoResourceOwnerTest.php | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php index 9981502c3..16f5eb51a 100644 --- a/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php +++ b/tests/OAuth/ResourceOwner/AmazonCognitoResourceOwnerTest.php @@ -12,9 +12,11 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AmazonCognitoResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Test\OAuth\ResourceOwner\GenericOAuth2ResourceOwnerTestCase; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\HttpUtils; final class AmazonCognitoResourceOwnerTest extends GenericOAuth2ResourceOwnerTestCase { @@ -37,11 +39,8 @@ final class AmazonCognitoResourceOwnerTest extends GenericOAuth2ResourceOwnerTes public function testGetUserInformation(): void { $resourceOwner = $this->createResourceOwner( - [ - 'domain' => 'test.com', - 'region' => 'us-east-1', - ], - $this->paths, + [], + [], [ $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), ] @@ -52,7 +51,6 @@ public function testGetUserInformation(): void */ $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); - $this->assertEquals('111', $userResponse->getUserIdentifier()); $this->assertEquals('baz@example.com', $userResponse->getEmail()); $this->assertEquals('bar', $userResponse->getRealName()); $this->assertNull($userResponse->getFirstName()); @@ -65,10 +63,7 @@ public function testGetUserInformationFailure(): void $this->expectException(AuthenticationException::class); $resourceOwner = $this->createResourceOwner( - [ - 'domain' => 'test.com', - 'region' => 'us-east-1', - ], + [], [], [ $this->createMockResponse('invalid', 'application/json; charset=utf-8', 401), @@ -77,4 +72,20 @@ public function testGetUserInformationFailure(): void $resourceOwner->getUserInformation($this->tokenData); } + + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface + { + return parent::setUpResourceOwner( + $name, + $httpUtils, + array_merge( + [ + 'domain' => 'test.com', + 'region' => 'us-west-2', + ], + $options + ), + $responses + ); + } }