diff --git a/app/config/collections.php b/app/config/collections.php index 526ed9f954..c4f0208618 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -681,6 +681,17 @@ $commonCollections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('expire'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], ], 'indexes' => [ [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2bc2096855..e049ccf4e2 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -236,10 +236,8 @@ App::post('/v1/account/sessions/email') $user->setAttributes($profile->getArrayCopy()); $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); $session = new Document(array_merge( [ @@ -252,6 +250,7 @@ App::post('/v1/account/sessions/email') 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + 'expire' => DateTime::addSeconds(new \DateTime(), $duration) ], $detector->getOS(), $detector->getClient(), @@ -283,6 +282,8 @@ App::post('/v1/account/sessions/email') ; } + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); + $response ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) @@ -294,7 +295,6 @@ App::post('/v1/account/sessions/email') $session ->setAttribute('current', true) ->setAttribute('countryName', $countryName) - ->setAttribute('expire', $expire) ->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '') ; @@ -596,8 +596,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $current = Auth::sessionVerify($sessions, Auth::$secret, $authDuration); + $current = Auth::sessionVerify($sessions, Auth::$secret); if ($current) { // Delete current session of new one. $currentDocument = $dbForProject->getDocument('sessions', $current); @@ -829,6 +828,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + 'expire' => DateTime::addSeconds(new \DateTime(), $duration) ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); $session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [ @@ -867,6 +867,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $state['success']['query'] = URLParser::unparseQuery($query); $state['success'] = URLParser::unparse($state['success']); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); + $response ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') ->addHeader('Pragma', 'no-cache') @@ -1229,7 +1231,6 @@ $createSession = function (string $userId, string $secret, Request $request, Res $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); $sessionSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $session = new Document(array_merge( [ @@ -1241,6 +1242,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + 'expire' => DateTime::addSeconds(new \DateTime(), $duration) ], $detector->getOS(), $detector->getClient(), @@ -1282,6 +1284,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $sessionSecret)])); } + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $protocol = $request->getProtocol(); $response @@ -1613,7 +1616,6 @@ App::post('/v1/account/sessions/anonymous') $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $session = new Document(array_merge( [ @@ -1625,6 +1627,7 @@ App::post('/v1/account/sessions/anonymous') 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + 'expire' => DateTime::addSeconds(new \DateTime(), $duration) ], $detector->getOS(), $detector->getClient(), @@ -1650,6 +1653,8 @@ App::post('/v1/account/sessions/anonymous') $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); } + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); + $response ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) @@ -1661,7 +1666,6 @@ App::post('/v1/account/sessions/anonymous') $session ->setAttribute('current', true) ->setAttribute('countryName', $countryName) - ->setAttribute('expire', $expire) ->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '') ; @@ -1849,16 +1853,13 @@ App::get('/v1/account/sessions') ->action(function (Response $response, Document $user, Locale $locale, Document $project) { $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $current = Auth::sessionVerify($sessions, Auth::$secret, $authDuration); + $current = Auth::sessionVerify($sessions, Auth::$secret); foreach ($sessions as $key => $session) {/** @var Document $session */ $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session->setAttribute('countryName', $countryName); $session->setAttribute('current', ($current == $session->getId()) ? true : false); - $session->setAttribute('expire', DateTime::formatTz(DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $authDuration))); - $sessions[$key] = $session; } @@ -1952,9 +1953,8 @@ App::get('/v1/account/sessions/:sessionId') ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Database $dbForProject, Document $project) { $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; foreach ($sessions as $session) {/** @var Document $session */ @@ -1964,7 +1964,6 @@ App::get('/v1/account/sessions/:sessionId') $session ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret))) ->setAttribute('countryName', $countryName) - ->setAttribute('expire', DateTime::formatTz(DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $authDuration))) ; return $response->dynamic($session, Response::MODEL_SESSION); @@ -2346,9 +2345,8 @@ App::delete('/v1/account/sessions/:sessionId') ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Document $project) { $protocol = $request->getProtocol(); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -2396,7 +2394,7 @@ App::delete('/v1/account/sessions/:sessionId') }); App::patch('/v1/account/sessions/:sessionId') - ->desc('Update OAuth session (refresh tokens)') + ->desc('Update (or renew) a session') ->groups(['api', 'account']) ->label('scope', 'accounts.write') ->label('event', 'users.[userId].sessions.[sessionId].update') @@ -2413,72 +2411,63 @@ App::patch('/v1/account/sessions/:sessionId') ->label('sdk.response.model', Response::MODEL_SESSION) ->label('abuse-limit', 10) ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to update the current device session.') - ->inject('request') ->inject('response') ->inject('user') ->inject('dbForProject') ->inject('project') - ->inject('locale') ->inject('queueForEvents') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Event $queueForEvents) { - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration) - : $sessionId; + ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents) { + $sessionId = ($sessionId === 'current') + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + : $sessionId; $sessions = $user->getAttribute('sessions', []); - foreach ($sessions as $key => $session) {/** @var Document $session */ - if ($sessionId == $session->getId()) { - // Comment below would skip re-generation if token is still valid - // We decided to not include this because developer can get expiration date from the session - // I kept code in comment because it might become relevant in the future - - // $expireAt = (int) $session->getAttribute('providerAccessTokenExpiry'); - // if(\time() < $expireAt - 5) { // 5 seconds time-sync and networking gap, to be safe - // return $response->noContent(); - // } - - $provider = $session->getAttribute('provider'); - $refreshToken = $session->getAttribute('providerRefreshToken'); - - $appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? ''; - $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; - - $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); - - if (!\class_exists($className)) { - throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); - } - - $oauth2 = new $className($appId, $appSecret, '', [], []); - - $oauth2->refreshTokens($refreshToken); - - $session - ->setAttribute('providerAccessToken', $oauth2->getAccessToken('')) - ->setAttribute('providerRefreshToken', $oauth2->getRefreshToken('')) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); - - $dbForProject->updateDocument('sessions', $sessionId, $session); - - $dbForProject->deleteCachedDocument('users', $user->getId()); - - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - - $session->setAttribute('expire', DateTime::formatTz(DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $authDuration))); - - $queueForEvents - ->setParam('userId', $user->getId()) - ->setParam('sessionId', $session->getId()) - ->setPayload($response->output($session, Response::MODEL_SESSION)) - ; - - return $response->dynamic($session, Response::MODEL_SESSION); + $session = null; + foreach ($sessions as $key => $value) { + if ($sessionId === $value->getId()) { + $session = $value; + break; } } - throw new Exception(Exception::USER_SESSION_NOT_FOUND); + if ($session === null) { + throw new Exception(Exception::USER_SESSION_NOT_FOUND); + } + + // Extend session + $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $session->setAttribute('expire', DateTime::addSeconds(new \DateTime(), $authDuration)); + + // Refresh OAuth access token + $provider = $session->getAttribute('provider', ''); + $refreshToken = $session->getAttribute('providerRefreshToken', ''); + $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); + + if (!empty($provider) && \class_exists($className)) { + $appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? ''; + $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; + + $oauth2 = new $className($appId, $appSecret, '', [], []); + $oauth2->refreshTokens($refreshToken); + + $session + ->setAttribute('providerAccessToken', $oauth2->getAccessToken('')) + ->setAttribute('providerRefreshToken', $oauth2->getRefreshToken('')) + ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); + } + + // Save changes + $dbForProject->updateDocument('sessions', $sessionId, $session); + $dbForProject->deleteCachedDocument('users', $user->getId()); + + $queueForEvents + ->setParam('userId', $user->getId()) + ->setParam('sessionId', $session->getId()) + ->setPayload($response->output($session, Response::MODEL_SESSION)) + ; + + return $response->dynamic($session, Response::MODEL_SESSION); }); App::delete('/v1/account/sessions') @@ -2521,7 +2510,6 @@ App::delete('/v1/account/sessions') if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { $session->setAttribute('current', true); - $session->setAttribute('expire', DateTime::addSeconds(new \DateTime($session->getCreatedAt()), Auth::TOKEN_EXPIRATION_LOGIN_LONG)); // If current session delete the cookies too $response diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 8f40e88b3d..27332b8bf3 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -962,6 +962,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + 'expire' => DateTime::addSeconds(new \DateTime(), $authDuration) ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); $session = $dbForProject->createDocument('sessions', $session diff --git a/app/init.php b/app/init.php index bc1db0b090..5e2aab6ba5 100644 --- a/app/init.php +++ b/app/init.php @@ -1108,11 +1108,9 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons Authorization::setDefaultStatus(true); Auth::setCookieName('a_session_' . $project->getId()); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; if (APP_MODE_ADMIN === $mode) { Auth::setCookieName('a_session_' . $console->getId()); - $authDuration = Auth::TOKEN_EXPIRATION_LOGIN_LONG; } $session = Auth::decodeSession( @@ -1164,7 +1162,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons if ( $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration) + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) ) { // Validate user has valid login token $user = new Document([]); } diff --git a/app/realtime.php b/app/realtime.php index f7fc7070a4..663e8c9e7f 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -545,11 +545,10 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re Auth::$secret = $session['secret'] ?? ''; $user = $database->getDocument('users', Auth::$unique); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; if ( empty($user->getId()) // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration) // Validate user has valid login token + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token ) { // cookie not valid throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); diff --git a/docs/references/account/update-session.md b/docs/references/account/update-session.md index 98080fda55..55226fe0bd 100644 --- a/docs/references/account/update-session.md +++ b/docs/references/account/update-session.md @@ -1 +1 @@ -Access tokens have limited lifespan and expire to mitigate security risks. If session was created using an OAuth provider, this route can be used to "refresh" the access token. \ No newline at end of file +Extend session's expiry to increase it's lifespan. Extending a session is useful when session length is short such as 5 minutes. \ No newline at end of file diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 86e0ba676b..41f4a75183 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -375,11 +375,10 @@ class Auth * * @param array $sessions * @param string $secret - * @param string $expires * * @return bool|string */ - public static function sessionVerify(array $sessions, string $secret, int $expires) + public static function sessionVerify(array $sessions, string $secret) { foreach ($sessions as $session) { /** @var Document $session */ @@ -387,7 +386,7 @@ class Auth $session->isSet('secret') && $session->isSet('provider') && $session->getAttribute('secret') === self::hash($secret) && - DateTime::formatTz(DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $expires)) >= DateTime::formatTz(DateTime::now()) + DateTime::formatTz(DateTime::format(new \DateTime($session->getAttribute('expire')))) >= DateTime::formatTz(DateTime::now()) ) { return $session->getId(); } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4d844c7551..8c4c8889f4 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -691,7 +691,7 @@ class ProjectsConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'duration' => 60, // Set session duration to 2 minutes + 'duration' => 60, // Set session duration to 1 minute ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -765,6 +765,61 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(401, $response['headers']['status-code']); + // Set session duration to 15s + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/duration', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'duration' => 15, // seconds + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(15, $response['body']['authDuration']); + + // Create session + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ]), [ + 'email' => $userEmail, + 'password' => 'password', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $sessionCookie = $response['headers']['set-cookie']; + + // Wait 10 seconds, ensure valid session, extend session + \sleep(10); + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'Cookie' => $sessionCookie, + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => $sessionCookie, + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Wait 20 seconds, ensure non-valid session + \sleep(20); + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'Cookie' => $sessionCookie, + ])); + + $this->assertEquals(401, $response['headers']['status-code']); + // Return project back to normal $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/duration', array_merge([ 'content-type' => 'application/json', diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 3fe906aaa3..ea6e9dc6c1 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -213,12 +213,14 @@ class AuthTest extends TestCase 'secret' => $hash, 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), ]), new Document([ '$id' => ID::custom('token2'), 'secret' => 'secret2', 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), ]), ]; @@ -230,19 +232,21 @@ class AuthTest extends TestCase 'secret' => $hash, 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), ]), new Document([ '$id' => ID::custom('token2'), 'secret' => 'secret2', 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), ]), ]; - $this->assertEquals(Auth::sessionVerify($tokens1, $secret, $expireTime1), 'token1'); - $this->assertEquals(Auth::sessionVerify($tokens1, 'false-secret', $expireTime1), false); - $this->assertEquals(Auth::sessionVerify($tokens2, $secret, $expireTime2), false); - $this->assertEquals(Auth::sessionVerify($tokens2, 'false-secret', $expireTime2), false); + $this->assertEquals(Auth::sessionVerify($tokens1, $secret), 'token1'); + $this->assertEquals(Auth::sessionVerify($tokens1, 'false-secret'), false); + $this->assertEquals(Auth::sessionVerify($tokens2, $secret), false); + $this->assertEquals(Auth::sessionVerify($tokens2, 'false-secret'), false); } public function testTokenVerify(): void