mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Restore full functionality of OAuth2 server [skip ci]
This commit is contained in:
		| @@ -3,8 +3,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\functional\_steps; | ||||
|  | ||||
| use api\components\OAuth2\Repositories\ScopeStorage as S; | ||||
| use api\tests\_pages\OauthRoute; | ||||
| use api\components\OAuth2\Repositories\PublicScopeRepository; | ||||
| use api\tests\FunctionalTester; | ||||
|  | ||||
| class OauthSteps extends FunctionalTester { | ||||
| @@ -32,31 +31,29 @@ class OauthSteps extends FunctionalTester { | ||||
|     } | ||||
|  | ||||
|     public function getRefreshToken(array $permissions = []): string { | ||||
|         $authCode = $this->obtainAuthCode(array_merge([S::OFFLINE_ACCESS], $permissions)); | ||||
|         $authCode = $this->obtainAuthCode(array_merge([PublicScopeRepository::OFFLINE_ACCESS], $permissions)); | ||||
|         $response = $this->issueToken($authCode); | ||||
|  | ||||
|         return $response['refresh_token']; | ||||
|     } | ||||
|  | ||||
|     public function issueToken($authCode): array { | ||||
|         $route = new OauthRoute($this); | ||||
|         $route->issueToken([ | ||||
|     public function issueToken(string $authCode): array { | ||||
|         $this->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'authorization_code', | ||||
|             'code' => $authCode, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'redirect_uri' => 'http://ely.by', | ||||
|             'grant_type' => 'authorization_code', | ||||
|         ]); | ||||
|  | ||||
|         return json_decode($this->grabResponse(), true); | ||||
|     } | ||||
|  | ||||
|     public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true): string { | ||||
|         $route = new OauthRoute($this); | ||||
|         $route->issueToken([ | ||||
|     public function getAccessTokenByClientCredentialsGrant(array $permissions = [], bool $useTrusted = true): string { | ||||
|         $this->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => $useTrusted ? 'trusted-client' : 'default-client', | ||||
|             'client_secret' => $useTrusted ? 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9' : 'AzWRy7ZjS1yRQUk2vRBDic8fprOKDB1W', | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'scope' => implode(',', $permissions), | ||||
|         ]); | ||||
|  | ||||
|   | ||||
| @@ -81,10 +81,10 @@ class AccessTokenCest { | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'redirect_uri' => 'http://some-other.domain', | ||||
|         ]); | ||||
|         $I->canSeeResponseCodeIs(400); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|             'message' => 'Client authentication failed.', | ||||
|             'message' => 'Client authentication failed', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -183,7 +183,7 @@ class AuthCodeCest { | ||||
|             'redirect_uri' => 'http://ely.by', | ||||
|             'response_type' => 'code', | ||||
|             'scope' => 'minecraft_server_session block_account', | ||||
|         ])); | ||||
|         ]), ['accept' => true]); // TODO: maybe remove? | ||||
|         $I->canSeeResponseCodeIs(400); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|   | ||||
| @@ -1,120 +1,87 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\functional\oauth; | ||||
|  | ||||
| use api\tests\_pages\OauthRoute; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
|  | ||||
| class ClientCredentialsCest { | ||||
|  | ||||
|     /** | ||||
|      * @var OauthRoute | ||||
|      */ | ||||
|     private $route; | ||||
|  | ||||
|     public function _before(FunctionalTester $I) { | ||||
|         $this->route = new OauthRoute($I); | ||||
|     public function issueTokenWithPublicScopes(FunctionalTester $I) { | ||||
|         $I->wantTo('issue token as not trusted client and require only public scopes'); | ||||
|         // We don't have any public scopes yet for this grant, so the test runs with an empty set | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => '', | ||||
|         ]); | ||||
|         $this->assertSuccessResponse($I); | ||||
|     } | ||||
|  | ||||
|     public function testIssueTokenWithWrongArgs(FunctionalTester $I) { | ||||
|         $I->wantTo('check behavior on on request without any credentials'); | ||||
|         $this->route->issueToken($this->buildParams()); | ||||
|         $I->canSeeResponseCodeIs(400); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_request', | ||||
|     public function issueTokenWithInternalScopesAsNotTrustedClient(FunctionalTester $I) { | ||||
|         $I->wantTo('issue token as not trusted client and require some internal scope'); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'block_account', | ||||
|         ]); | ||||
|  | ||||
|         $I->wantTo('check behavior on passing invalid client_id'); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'invalid-client', | ||||
|             'invalid-secret', | ||||
|             ['invalid-scope'] | ||||
|         )); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|         ]); | ||||
|  | ||||
|         $I->wantTo('check behavior on passing invalid client_secret'); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'ely', | ||||
|             'invalid-secret', | ||||
|             ['invalid-scope'] | ||||
|         )); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|         ]); | ||||
|  | ||||
|         $I->wantTo('check behavior on passing invalid client_secret'); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'ely', | ||||
|             'invalid-secret', | ||||
|             ['invalid-scope'] | ||||
|         )); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function testIssueTokenWithPublicScopes(OauthSteps $I) { | ||||
|         // TODO: we don't have any public scopes yet for this grant, so the test runs with an empty set | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             [] | ||||
|         )); | ||||
|         $I->canSeeResponseCodeIs(200); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'token_type' => 'Bearer', | ||||
|         ]); | ||||
|         $I->canSeeResponseJsonMatchesJsonPath('$.access_token'); | ||||
|         $I->canSeeResponseJsonMatchesJsonPath('$.expires_in'); | ||||
|     } | ||||
|  | ||||
|     public function testIssueTokenWithInternalScopes(OauthSteps $I) { | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             ['account_block'] | ||||
|         )); | ||||
|         $I->canSeeResponseCodeIs(400); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_scope', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'trusted-client', | ||||
|             'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9', | ||||
|             ['account_block'] | ||||
|         )); | ||||
|     public function issueTokenWithInternalScopesAsTrustedClient(OauthSteps $I) { | ||||
|         $I->wantTo('issue token as trusted client and require some internal scope'); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => 'trusted-client', | ||||
|             'client_secret' => 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9', | ||||
|             'scope' => 'block_account', | ||||
|         ]); | ||||
|         $this->assertSuccessResponse($I); | ||||
|     } | ||||
|  | ||||
|     public function issueTokenByPassingInvalidClientId(FunctionalTester $I) { | ||||
|         $I->wantToTest('behavior on passing invalid client_id'); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => 'invalid-client', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'block_account', | ||||
|         ]); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function issueTokenByPassingInvalidClientSecret(FunctionalTester $I) { | ||||
|         $I->wantTo('check behavior on passing invalid client_secret'); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'client_credentials', | ||||
|             'client_id' => 'trusted-client', | ||||
|             'client_secret' => 'invalid-secret', | ||||
|             'scope' => 'block_account', | ||||
|         ]); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_client', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     private function assertSuccessResponse(FunctionalTester $I): void { | ||||
|         $I->canSeeResponseCodeIs(200); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'token_type' => 'Bearer', | ||||
|         ]); | ||||
|         $I->canSeeResponseJsonMatchesJsonPath('$.access_token'); | ||||
|         $I->canSeeResponseJsonMatchesJsonPath('$.expires_in'); | ||||
|     } | ||||
|  | ||||
|     private function buildParams($clientId = null, $clientSecret = null, array $scopes = null) { | ||||
|         $params = ['grant_type' => 'client_credentials']; | ||||
|         if ($clientId !== null) { | ||||
|             $params['client_id'] = $clientId; | ||||
|         } | ||||
|  | ||||
|         if ($clientSecret !== null) { | ||||
|             $params['client_secret'] = $clientSecret; | ||||
|         } | ||||
|  | ||||
|         if ($scopes !== null) { | ||||
|             $params['scope'] = implode(',', $scopes); | ||||
|         } | ||||
|  | ||||
|         return $params; | ||||
|         $I->cantSeeResponseJsonMatchesJsonPath('$.refresh_token'); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,83 +1,83 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\functional\oauth; | ||||
|  | ||||
| use api\components\OAuth2\Repositories\ScopeStorage as S; | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\OauthRoute; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
|  | ||||
| class RefreshTokenCest { | ||||
|  | ||||
|     /** | ||||
|      * @var OauthRoute | ||||
|      */ | ||||
|     private $route; | ||||
|  | ||||
|     public function _before(FunctionalTester $I) { | ||||
|         $this->route = new OauthRoute($I); | ||||
|     public function refreshToken(OauthSteps $I) { | ||||
|         $I->wantTo('refresh token without passing the desired scopes'); | ||||
|         $refreshToken = $I->getRefreshToken(); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => $refreshToken, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|         ]); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidRefreshToken(OauthSteps $I) { | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             'some-invalid-refresh-token', | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM' | ||||
|         )); | ||||
|     public function refreshTokenWithSameScopes(OauthSteps $I) { | ||||
|         $refreshToken = $I->getRefreshToken(['minecraft_server_session']); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => $refreshToken, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'minecraft_server_session offline_access', | ||||
|         ]); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function refreshTokenTwice(OauthSteps $I) { | ||||
|         $I->wantTo('refresh token two times in a row and ensure, that token isn\'t rotating'); | ||||
|         $refreshToken = $I->getRefreshToken(['minecraft_server_session']); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => $refreshToken, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'minecraft_server_session', | ||||
|         ]); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|  | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => $refreshToken, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'minecraft_server_session', | ||||
|         ]); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function passInvalidRefreshToken(OauthSteps $I) { | ||||
|         $I->wantToTest('behaviour of the server when invalid refresh token passed'); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => 'some-invalid-refresh-token', | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|         ]); | ||||
|         $I->canSeeResponseCodeIs(401); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'error' => 'invalid_request', | ||||
|             'message' => 'The refresh token is invalid.', | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function testRefreshToken(OauthSteps $I) { | ||||
|         $refreshToken = $I->getRefreshToken(); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             $refreshToken, | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM' | ||||
|         )); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function testRefreshTokenWithSameScopes(OauthSteps $I) { | ||||
|         $refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             $refreshToken, | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             [P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS] | ||||
|         )); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function testRefreshTokenTwice(OauthSteps $I) { | ||||
|         $refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             $refreshToken, | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             [P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS] | ||||
|         )); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|  | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             $refreshToken, | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             [P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS] | ||||
|         )); | ||||
|         $this->canSeeRefreshTokenSuccess($I); | ||||
|     } | ||||
|  | ||||
|     public function testRefreshTokenWithNewScopes(OauthSteps $I) { | ||||
|         $refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]); | ||||
|         $this->route->issueToken($this->buildParams( | ||||
|             $refreshToken, | ||||
|             'ely', | ||||
|             'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             [P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS, 'account_email'] | ||||
|         )); | ||||
|     public function requireNewScopes(OauthSteps $I) { | ||||
|         $I->wantToTest('behavior when required the new scope that was not issued with original token'); | ||||
|         $refreshToken = $I->getRefreshToken(['minecraft_server_session']); | ||||
|         $I->sendPOST('/api/oauth2/v1/token', [ | ||||
|             'grant_type' => 'refresh_token', | ||||
|             'refresh_token' => $refreshToken, | ||||
|             'client_id' => 'ely', | ||||
|             'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM', | ||||
|             'scope' => 'minecraft_server_session account_email', | ||||
|         ]); | ||||
|         $I->canSeeResponseCodeIs(400); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
| @@ -85,34 +85,8 @@ class RefreshTokenCest { | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     private function buildParams($refreshToken = null, $clientId = null, $clientSecret = null, $scopes = []) { | ||||
|         $params = ['grant_type' => 'refresh_token']; | ||||
|         if ($refreshToken !== null) { | ||||
|             $params['refresh_token'] = $refreshToken; | ||||
|         } | ||||
|  | ||||
|         if ($clientId !== null) { | ||||
|             $params['client_id'] = $clientId; | ||||
|         } | ||||
|  | ||||
|         if ($clientSecret !== null) { | ||||
|             $params['client_secret'] = $clientSecret; | ||||
|         } | ||||
|  | ||||
|         if (!empty($scopes)) { | ||||
|             if (is_array($scopes)) { | ||||
|                 $scopes = implode(',', $scopes); | ||||
|             } | ||||
|  | ||||
|             $params['scope'] = $scopes; | ||||
|         } | ||||
|  | ||||
|         return $params; | ||||
|     } | ||||
|  | ||||
|     private function canSeeRefreshTokenSuccess(OauthSteps $I) { | ||||
|         $I->canSeeResponseCodeIs(200); | ||||
|         $I->canSeeResponseIsJson(); | ||||
|         $I->canSeeResponseContainsJson([ | ||||
|             'token_type' => 'Bearer', | ||||
|         ]); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user