Return user field when requestUser param received on authentication/refresh endpoint [deploy]

This commit is contained in:
ErickSkrauch 2021-03-06 10:37:58 +01:00
parent 077db4f328
commit 1aed8f59cb
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
6 changed files with 149 additions and 61 deletions

View File

@ -4,39 +4,33 @@ declare(strict_types=1);
namespace api\modules\authserver\models;
use common\models\Account;
use Lcobucci\JWT\Token;
class AuthenticateData {
final class AuthenticateData {
/**
* @var Account
*/
private $account;
private Account $account;
/**
* @var Token
*/
private $accessToken;
private string $accessToken;
/**
* @var string
*/
private $clientToken;
private string $clientToken;
public function __construct(Account $account, string $accessToken, string $clientToken) {
private bool $requestUser;
public function __construct(Account $account, string $accessToken, string $clientToken, bool $requestUser) {
$this->account = $account;
$this->accessToken = $accessToken;
$this->clientToken = $clientToken;
$this->requestUser = $requestUser;
}
public function getResponseData(bool $includeAvailableProfiles = false): array {
$uuid = str_replace('-', '', $this->account->uuid);
$result = [
'accessToken' => $this->accessToken,
'clientToken' => $this->clientToken,
'selectedProfile' => [
'id' => str_replace('-', '', $this->account->uuid),
// Might contain a lot more fields, but even Mojang returns only those:
'id' => $uuid,
'name' => $this->account->username,
'legacy' => false,
],
];
@ -46,6 +40,20 @@ class AuthenticateData {
$result['availableProfiles'] = $availableProfiles;
}
if ($this->requestUser) {
// There are a lot of fields, but even Mojang returns only those:
$result['user'] = [
'id' => $uuid,
'username' => $this->account->username,
'properties' => [
[
'name' => 'preferredLanguage',
'value' => $this->account->lang,
],
],
];
}
return $result;
}

View File

@ -34,10 +34,16 @@ class AuthenticationForm extends ApiForm {
*/
public $clientToken;
/**
* @var string|bool
*/
public $requestUser;
public function rules(): array {
return [
[['username', 'password', 'clientToken'], RequiredValidator::class],
[['clientToken'], ClientTokenValidator::class],
[['requestUser'], 'boolean'],
];
}
@ -85,7 +91,7 @@ class AuthenticationForm extends ApiForm {
/** @var Account $account */
$account = $loginForm->getAccount();
$token = Yii::$app->tokensFactory->createForMinecraftAccount($account, $this->clientToken);
$dataModel = new AuthenticateData($account, (string)$token, $this->clientToken);
$dataModel = new AuthenticateData($account, (string)$token, $this->clientToken, (bool)$this->requestUser);
/** @var OauthSession|null $minecraftOauthSession */
$minecraftOauthSession = $account->getOauthSessions()
->andWhere(['client_id' => OauthClient::UNAUTHORIZED_MINECRAFT_GAME_LAUNCHER])

View File

@ -28,10 +28,16 @@ class RefreshTokenForm extends ApiForm {
*/
public $clientToken;
/**
* @var string|bool
*/
public $requestUser;
public function rules(): array {
return [
[['accessToken', 'clientToken'], RequiredValidator::class],
[['accessToken'], AccessTokenValidator::class, 'verifyExpiration' => false],
[['requestUser'], 'boolean'],
];
}
@ -83,7 +89,7 @@ class RefreshTokenForm extends ApiForm {
$minecraftOauthSession->last_used_at = time();
Assert::true($minecraftOauthSession->save());
return new AuthenticateData($account, (string)$token, $this->clientToken);
return new AuthenticateData($account, (string)$token, $this->clientToken, (bool)$this->requestUser);
}
}

View File

@ -10,33 +10,70 @@ use Ramsey\Uuid\Uuid;
class AuthorizationCest {
/**
* # Matrix:
* # * login: username/email
* # * requestUser: true/false
* # * json: true/false
*
* JSON: false
* @example {"login": "admin", "password": "password_0"}
* @example {"login": "admin", "password": "password_0", "requestUser": true}
* @example {"login": "admin@ely.by", "password": "password_0"}
* @example {"login": "admin@ely.by", "password": "password_0", "requestUser": true}
*
* JSON: true
* @example {"json": true, "login": "admin", "password": "password_0"}
* @example {"json": true, "login": "admin", "password": "password_0", "requestUser": true}
* @example {"json": true, "login": "admin@ely.by", "password": "password_0"}
* @example {"json": true, "login": "admin@ely.by", "password": "password_0", "requestUser": true}
*/
public function byFormParamsPostRequest(FunctionalTester $I, Example $example) {
$I->wantTo('authenticate by username and password');
$I->sendPOST('/api/authserver/authentication/authenticate', [
'username' => $example['login'],
'password' => $example['password'],
public function authenticate(FunctionalTester $I, Example $case) {
$params = [
'username' => $case['login'],
'password' => $case['password'],
'clientToken' => Uuid::uuid4()->toString(),
];
if ($case['requestUser'] ?? false) {
$params['requestUser'] = true;
}
if ($case['json'] ?? false) {
$params = json_encode($params);
}
$I->sendPOST('/api/authserver/authentication/authenticate', $params);
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
$I->canSeeResponseContainsJson([
'selectedProfile' => [
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
'availableProfiles' => [
[
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
],
]);
$this->testSuccessResponse($I);
}
/**
* @example {"login": "admin", "password": "password_0"}
* @example {"login": "admin@ely.by", "password": "password_0"}
*/
public function byJsonPostRequest(FunctionalTester $I, Example $example) {
$I->wantTo('authenticate by username and password sent via post body');
$I->sendPOST('/api/authserver/authentication/authenticate', json_encode([
'username' => $example['login'],
'password' => $example['password'],
'clientToken' => Uuid::uuid4()->toString(),
]));
$this->testSuccessResponse($I);
if ($case['requestUser'] ?? false) {
$I->canSeeResponseContainsJson([
'user' => [
'id' => 'df936908b2e1544d96f82977ec213022',
'username' => 'Admin',
'properties' => [
[
'name' => 'preferredLanguage',
'value' => 'en',
],
],
],
]);
} else {
$I->cantSeeResponseJsonMatchesJsonPath('$.user');
}
}
public function byEmailWithEnabledTwoFactorAuth(FunctionalTester $I) {
@ -125,17 +162,4 @@ class AuthorizationCest {
]);
}
private function testSuccessResponse(FunctionalTester $I) {
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].id');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].name');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].legacy');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
}
}

View File

@ -9,23 +9,33 @@ use Ramsey\Uuid\Uuid;
class RefreshCest {
public function refresh(AuthserverSteps $I) {
/**
* @example [true]
* @example [false]
*/
public function refresh(AuthserverSteps $I, Example $case) {
$I->wantTo('refresh accessToken');
[$accessToken, $clientToken] = $I->amAuthenticated();
$I->sendPOST('/api/authserver/authentication/refresh', [
'accessToken' => $accessToken,
'clientToken' => $clientToken,
'requestUser' => $case[0],
]);
$this->assertSuccessResponse($I);
$this->assertSuccessResponse($I, $case[0]);
}
public function refreshLegacyAccessToken(AuthserverSteps $I) {
/**
* @example [true]
* @example [false]
*/
public function refreshLegacyAccessToken(AuthserverSteps $I, Example $case) {
$I->wantTo('refresh legacy accessToken');
$I->sendPOST('/api/authserver/authentication/refresh', [
'accessToken' => 'e7bb6648-2183-4981-9b86-eba5e7f87b42',
'clientToken' => '6f380440-0c05-47bd-b7c6-d011f1b5308f',
'requestUser' => $case[0],
]);
$this->assertSuccessResponse($I);
$this->assertSuccessResponse($I, $case[0]);
}
public function refreshWithInvalidClientToken(AuthserverSteps $I) {
@ -63,7 +73,7 @@ class RefreshCest {
'accessToken' => $example['accessToken'],
'clientToken' => $example['clientToken'],
]);
$this->assertSuccessResponse($I);
$this->assertSuccessResponse($I, false);
}
public function wrongArguments(AuthserverSteps $I) {
@ -119,15 +129,36 @@ class RefreshCest {
]);
}
private function assertSuccessResponse(AuthserverSteps $I) {
private function assertSuccessResponse(AuthserverSteps $I, bool $requestUser) {
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
$I->canSeeResponseContainsJson([
'selectedProfile' => [
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
$I->cantSeeResponseJsonMatchesJsonPath('$.availableProfiles');
if ($requestUser) {
$I->canSeeResponseContainsJson([
'user' => [
'id' => 'df936908b2e1544d96f82977ec213022',
'username' => 'Admin',
'properties' => [
[
'name' => 'preferredLanguage',
'value' => 'en',
],
],
],
]);
} else {
$I->cantSeeResponseJsonMatchesJsonPath('$.user');
}
}
}

View File

@ -31,11 +31,24 @@ class AuthenticationFormTest extends TestCase {
$this->assertSame($authForm->clientToken, $result['clientToken']);
$this->assertSame('df936908b2e1544d96f82977ec213022', $result['selectedProfile']['id']);
$this->assertSame('Admin', $result['selectedProfile']['name']);
$this->assertFalse($result['selectedProfile']['legacy']);
$this->assertTrue(OauthSession::find()->andWhere([
'account_id' => 1,
'client_id' => OauthClient::UNAUTHORIZED_MINECRAFT_GAME_LAUNCHER,
])->exists());
$this->assertArrayNotHasKey('user', $result);
$authForm->requestUser = true;
$result = $authForm->authenticate()->getResponseData();
$this->assertSame([
'id' => 'df936908b2e1544d96f82977ec213022',
'username' => 'Admin',
'properties' => [
[
'name' => 'preferredLanguage',
'value' => 'en',
],
],
], $result['user']);
}
/**