diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 8b32349957..78988f525b 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -2109,7 +2109,7 @@ App::post('/v1/users/:userId/jwts') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_JWT) ->param('userId', '', new UID(), 'User ID.') - ->param('sessionId', 'recent', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true) + ->param('sessionId', '', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true) ->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true) ->inject('response') ->inject('dbForProject') @@ -2137,17 +2137,13 @@ App::post('/v1/users/:userId/jwts') } } - if ($session->isEmpty()) { - throw new Exception(Exception::USER_SESSION_NOT_FOUND); - } - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0); $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic(new Document(['jwt' => $jwt->encode([ 'userId' => $user->getId(), - 'sessionId' => $session->getId() + 'sessionId' => $session->isEmpty() ? '' : $session->getId() ])]), Response::MODEL_JWT); }); diff --git a/app/init.php b/app/init.php index ef7742956f..c6a0680a9d 100644 --- a/app/init.php +++ b/app/init.php @@ -1242,14 +1242,15 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons } $jwtUserId = $payload['userId'] ?? ''; - $jwtSessionId = $payload['sessionId'] ?? ''; - - if ($jwtUserId && $jwtSessionId) { + if (!empty($jwtUserId)) { $user = $dbForProject->getDocument('users', $jwtUserId); } - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); + $jwtSessionId = $payload['sessionId'] ?? ''; + if(!empty($jwtSessionId)) { + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } } } diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index c34227e4de..c06bc6f4a8 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -1589,6 +1589,27 @@ trait UsersBase ], false); $this->assertEquals($user['headers']['status-code'], 201); + // Create JWT 0, with no session available + $response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['jwt']); + $jwt0 = $response['body']['jwt']; + + // Ensure JWT 0 works + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-jwt' => $jwt0, + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($userId, $response['body']['$id']); + // Create two sessions $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ 'origin' => 'http://localhost', @@ -1641,12 +1662,13 @@ trait UsersBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($userId, $response['body']['$id']); - // Create JWT 2 for latest session using default param + // Create JWT 2 for latest session using 'current' param $response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'duration' => 5 + 'duration' => 5, + 'sessionId' => 'current' ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -1696,6 +1718,27 @@ trait UsersBase $this->assertEquals(401, $response['headers']['status-code']); + // Ensure JWT 0 works still even with no sessions + + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId . '/sessions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'sessionId' => $session2Id + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-jwt' => $jwt0, + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($userId, $response['body']['$id']); + // Cleanup after test $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([