From e26230cc3447ac906c31028a5e42f2468dfff2c4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 12 Feb 2024 22:59:49 +1300 Subject: [PATCH 1/9] Disallow creating a session if one already exists --- app/config/errors.php | 2 +- app/controllers/api/account.php | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 79a092ec99..2f58e9d8e8 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -178,7 +178,7 @@ return [ ], Exception::USER_SESSION_ALREADY_EXISTS => [ 'name' => Exception::USER_SESSION_ALREADY_EXISTS, - 'description' => 'Creation of anonymous users is prohibited when a session is active.', + 'description' => 'Creation of a session is prohibited when a session is active.', 'code' => 401, ], Exception::USER_NOT_FOUND => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 49eabcbf5c..57b5a625db 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -222,6 +222,9 @@ App::post('/v1/account/sessions/email') ->inject('queueForEvents') ->inject('hooks') ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) { + if (!$user->isEmpty()) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -481,6 +484,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('geodb') ->inject('queueForEvents') ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { + if (!$user->isEmpty()) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); @@ -1447,6 +1453,10 @@ App::post('/v1/account/tokens/email') }); $createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + if (!$user->isEmpty()) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1505,7 +1515,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), - ])); + ]) + ); $dbForProject->purgeCachedDocument('users', $user->getId()); Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); @@ -1804,6 +1815,9 @@ App::post('/v1/account/sessions/anonymous') ->inject('geodb') ->inject('queueForEvents') ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { + if (!$user->isEmpty()) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } $protocol = $request->getProtocol(); $roles = Authorization::getRoles(); @@ -1814,10 +1828,6 @@ App::post('/v1/account/sessions/anonymous') throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user'); } - if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS, 'Cannot create an anonymous user when logged in'); - } - $limit = $project->getAttribute('auths', [])['limit'] ?? 0; if ($limit !== 0) { From 1c1a2eab2cc5ceba7d3609b1fd0299ac8bf8e5a3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 21 Feb 2024 03:16:01 +1300 Subject: [PATCH 2/9] Format --- app/controllers/api/account.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2e097bdbe7..ad1eb74811 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1527,8 +1527,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), - ]) - ); + ])); $dbForProject->purgeCachedDocument('users', $user->getId()); Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); From 353069bc1fcbbda8b40b6c0848afad51ab337205 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 21 Feb 2024 18:50:28 +1300 Subject: [PATCH 3/9] Fix GraphQL test relying on creating session when one exists --- tests/e2e/Services/GraphQL/BatchTest.php | 27 +++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/e2e/Services/GraphQL/BatchTest.php b/tests/e2e/Services/GraphQL/BatchTest.php index 55bce6eae3..7d154de802 100644 --- a/tests/e2e/Services/GraphQL/BatchTest.php +++ b/tests/e2e/Services/GraphQL/BatchTest.php @@ -281,19 +281,22 @@ class BatchTest extends Scope public function testQueryBatchedMutations() { $projectId = $this->getProject()['$id']; - $email = 'tester' . \uniqid() . '@example.com'; + $email1 = 'tester' . \uniqid() . '@example.com'; + $email2 = 'tester' . \uniqid() . '@example.com'; $graphQLPayload = [ - 'query' => 'mutation CreateAndLogin($userId: String!, $email: String!, $password: String!, $name: String) { - accountCreate(userId: $userId, email: $email, password: $password, name: $name) { - name + 'query' => 'mutation CreateAndLogin($user1Id: String!, $user2Id: String!, $email1: String!, $email2: String!, $password: String!, $name: String) { + account1: accountCreate(userId: $user1Id, email: $email1, password: $password, name: $name) { + email } - accountCreateEmailPasswordSession(email: $email, password: $password) { - expire + account2: accountCreate(userId: $user2Id, email: $email2, password: $password, name: $name) { + email } }', 'variables' => [ - 'userId' => ID::unique(), - 'email' => $email, + 'user1Id' => ID::unique(), + 'user2Id' => ID::unique(), + 'email1' => $email1, + 'email2' => $email2, 'password' => 'password', 'name' => 'Tester', ], @@ -304,12 +307,12 @@ class BatchTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertIsArray($response['body']['data']); $this->assertArrayNotHasKey('errors', $response['body']); - $this->assertArrayHasKey('accountCreate', $response['body']['data']); - $this->assertArrayHasKey('accountCreateEmailPasswordSession', $response['body']['data']); - $this->assertEquals('Tester', $response['body']['data']['accountCreate']['name']); + $this->assertArrayHasKey('account1', $response['body']['data']); + $this->assertArrayHasKey('account2', $response['body']['data']); + $this->assertEquals($email1, $response['body']['data']['account1']['email']); + $this->assertEquals($email2, $response['body']['data']['account2']['email']); } public function testQueryBatchedMutationsOfSameType() From a178dbfc4bfa08fe8e7e17a76ece1eeced505b2d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 25 Feb 2024 03:04:35 +1300 Subject: [PATCH 4/9] Allow existing session when upgrading from anonymous to oauth --- app/controllers/api/account.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 397ead456f..e15e13baa0 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -558,7 +558,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('queueForEvents') ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + $current = $user->find('current', true, 'sessions'); + + if ($current && $current->getAttribute('provider') !== Auth::SESSION_PROVIDER_ANONYMOUS) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } } $protocol = $request->getProtocol(); From d1f797ff23a4e9fbd35fd0ea0275ff0d64139330 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 25 Feb 2024 19:24:06 +1300 Subject: [PATCH 5/9] Fix test --- .../e2e/Services/Account/AccountCustomClientTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index d3fc13bd17..3e67d789e8 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -74,7 +74,9 @@ class AccountCustomClientTest extends Scope $this->assertEmpty($response['body']['secret']); $this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire'])); - // already logged in + /** + * Test for FAILURE + */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -85,11 +87,8 @@ class AccountCustomClientTest extends Scope 'password' => $password, ]); - $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(401, $response['headers']['status-code']); - /** - * Test for FAILURE - */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -2247,7 +2246,7 @@ class AccountCustomClientTest extends Scope $this->assertEmpty($response['body']['secret']); $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire'])); - \sleep(10); + \sleep(15); $smsRequest = $this->getLastRequest(); From 2ccfd896819cafd1fea169707fde2ebaa2e9ce4d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 26 Feb 2024 00:14:27 +1300 Subject: [PATCH 6/9] Fix count tests --- tests/e2e/Services/Account/AccountCustomClientTest.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 3e67d789e8..aea0cf917b 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -232,10 +232,7 @@ class AccountCustomClientTest extends Scope ])); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsArray($response['body']); - $this->assertNotEmpty($response['body']); - $this->assertCount(2, $response['body']); - $this->assertEquals(3, $response['body']['total']); + $this->assertEquals(2, $response['body']['total']); $this->assertEquals($sessionId, $response['body']['sessions'][0]['$id']); $this->assertEquals('Windows', $response['body']['sessions'][0]['osName']); @@ -292,7 +289,7 @@ class AccountCustomClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertIsArray($response['body']['logs']); $this->assertNotEmpty($response['body']['logs']); - $this->assertCount(4, $response['body']['logs']); + $this->assertCount(3, $response['body']['logs']); $this->assertIsNumeric($response['body']['total']); $this->assertEquals("session.create", $response['body']['logs'][2]['event']); $this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']); @@ -2238,7 +2235,6 @@ class AccountCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, - ])); $this->assertEquals(201, $response['headers']['status-code']); From 675dec48b347e61d359db399345a4a44463047a8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 26 Feb 2024 01:05:14 +1300 Subject: [PATCH 7/9] Fix tests --- .../Services/Account/AccountCustomClientTest.php | 15 +++++++-------- .../Services/Account/AccountCustomServerTest.php | 9 ++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index aea0cf917b..56cc126cbc 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -291,7 +291,7 @@ class AccountCustomClientTest extends Scope $this->assertNotEmpty($response['body']['logs']); $this->assertCount(3, $response['body']['logs']); $this->assertIsNumeric($response['body']['total']); - $this->assertEquals("session.create", $response['body']['logs'][2]['event']); + $this->assertEquals("user.create", $response['body']['logs'][2]['event']); $this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']); $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time'])); @@ -313,10 +313,6 @@ class AccountCustomClientTest extends Scope $this->assertEquals('--', $response['body']['logs'][1]['countryCode']); $this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']); - $this->assertEquals("user.create", $response['body']['logs'][3]['event']); - $this->assertEquals(filter_var($response['body']['logs'][3]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][3]['ip']); - $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time'])); - $this->assertEquals('Windows', $response['body']['logs'][2]['osName']); $this->assertEquals('WIN', $response['body']['logs'][2]['osCode']); $this->assertEquals('10', $response['body']['logs'][2]['osVersion']); @@ -368,7 +364,7 @@ class AccountCustomClientTest extends Scope $this->assertEquals($responseOffset['headers']['status-code'], 200); $this->assertIsArray($responseOffset['body']['logs']); $this->assertNotEmpty($responseOffset['body']['logs']); - $this->assertCount(3, $responseOffset['body']['logs']); + $this->assertCount(2, $responseOffset['body']['logs']); $this->assertIsNumeric($responseOffset['body']['total']); $this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]); @@ -2242,12 +2238,15 @@ class AccountCustomClientTest extends Scope $this->assertEmpty($response['body']['secret']); $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire'])); - \sleep(15); + \sleep(5); $smsRequest = $this->getLastRequest(); + $message = $smsRequest['data']['message']; + $token = substr($message, 0, 6); + return \array_merge($data, [ - 'token' => $smsRequest['data']['secret'] + 'token' => $token ]); } diff --git a/tests/e2e/Services/Account/AccountCustomServerTest.php b/tests/e2e/Services/Account/AccountCustomServerTest.php index e65e574535..e29110cf42 100644 --- a/tests/e2e/Services/Account/AccountCustomServerTest.php +++ b/tests/e2e/Services/Account/AccountCustomServerTest.php @@ -62,7 +62,9 @@ class AccountCustomServerTest extends Scope $this->assertNotEmpty($response['body']['secret']); $this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire'])); - // already logged in + /** + * Test for FAILURE + */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -72,11 +74,8 @@ class AccountCustomServerTest extends Scope 'password' => $password, ]); - $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(401, $response['headers']['status-code']); - /** - * Test for FAILURE - */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], From 91c52b28eb020c45a6a45ce692f57a2c4f68c398 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 26 Feb 2024 01:13:39 +1300 Subject: [PATCH 8/9] Move targets to new session if upgrading to oauth session --- app/controllers/api/account.php | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index caa48f0461..2df9f9229a 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -557,14 +557,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('geodb') ->inject('queueForEvents') ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { - if (!$user->isEmpty()) { - $current = $user->find('current', true, 'sessions'); - - if ($current && $current->getAttribute('provider') !== Auth::SESSION_PROVIDER_ANONYMOUS) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); - } - } - $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; @@ -686,6 +678,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') if (!empty($userWithMatchingEmail)) { throw new Exception(Exception::USER_ALREADY_EXISTS); } + + $sessionUpgrade = true; } $sessions = $user->getAttribute('sessions', []); @@ -715,7 +709,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } /** - * Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not. + * Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not. */ $isVerified = $oauth2->isEmailVerified($accessToken); @@ -958,6 +952,20 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } + if (isset($sessionUpgrade) && $sessionUpgrade) { + foreach ($user->getAttribute('targets', []) as $target) { + if ($target->getAttribute('providerType') !== MESSAGE_TYPE_PUSH) { + continue; + } + + $target + ->setAttribute('sessionId', $session->getId()) + ->setAttrubte('sessionInternalId', $session->getInternalId()); + + $dbForProject->updateDocument('targets', $target->getId(), $target); + } + } + $dbForProject->purgeCachedDocument('users', $user->getId()); $state['success']['query'] = URLParser::unparseQuery($query); From 6fb1e929cd53b755ddfecc55fb18f3111ade85c7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 27 Feb 2024 22:08:39 +1300 Subject: [PATCH 9/9] Use group hook to block recreating sessions --- app/controllers/api/account.php | 18 +++--------------- app/controllers/shared/api.php | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ebc347edc6..6c748bec5a 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -228,10 +228,6 @@ App::post('/v1/account/sessions/email') ->inject('queueForEvents') ->inject('hooks') ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) { - if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); - } - $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -1545,10 +1541,6 @@ App::post('/v1/account/tokens/email') }); $createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { - if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); - } - $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1658,7 +1650,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res App::put('/v1/account/sessions/magic-url') ->desc('Update magic URL session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -1688,7 +1680,7 @@ App::put('/v1/account/sessions/magic-url') App::put('/v1/account/sessions/phone') ->desc('Update phone session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -1718,7 +1710,7 @@ App::put('/v1/account/sessions/phone') App::post('/v1/account/sessions/token') ->desc('Create session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -1941,10 +1933,6 @@ App::post('/v1/account/sessions/anonymous') ->inject('geodb') ->inject('queueForEvents') ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { - if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); - } - $protocol = $request->getProtocol(); $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index e75161d035..0101d72116 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -461,6 +461,20 @@ App::init() } }); +App::init() + ->groups(['session']) + ->inject('user') + ->inject('request') + ->action(function (Document $user, Request $request) { + if (\str_contains($request->getURI(), 'oauth2')) { + return; + } + + if (!$user->isEmpty()) { + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + } + }); + /** * Limit user session * @@ -497,6 +511,7 @@ App::shutdown() $session = array_shift($sessions); $dbForProject->deleteDocument('sessions', $session->getId()); } + $dbForProject->purgeCachedDocument('users', $userId); });