Implement session extension

This commit is contained in:
Matej Bačo
2023-12-04 17:13:14 +01:00
parent 396ff87fbc
commit d85cb572b0
5 changed files with 78 additions and 23 deletions
+11
View File
@@ -681,6 +681,17 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('expire'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
],
'indexes' => [
[
+6 -12
View File
@@ -246,6 +246,7 @@ App::post('/v1/account/sessions/email')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => $expire
],
$detector->getOS(),
$detector->getClient(),
@@ -289,7 +290,6 @@ App::post('/v1/account/sessions/email')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
;
$queueForEvents
@@ -757,6 +757,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => $expire
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
if (empty($user->getAttribute('email'))) {
@@ -783,8 +784,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$dbForProject->deleteCachedDocument('users', $user->getId());
$session->setAttribute('expire', $expire);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
@@ -1175,6 +1174,7 @@ App::put('/v1/account/sessions/magic-url')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => $expire
],
$detector->getOS(),
$detector->getClient(),
@@ -1228,8 +1228,7 @@ App::put('/v1/account/sessions/magic-url')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire);
->setAttribute('countryName', $countryName);
$response->dynamic($session, Response::MODEL_SESSION);
});
@@ -1450,6 +1449,7 @@ App::put('/v1/account/sessions/phone')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => $expire
],
$detector->getOS(),
$detector->getClient(),
@@ -1504,7 +1504,6 @@ App::put('/v1/account/sessions/phone')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -1605,6 +1604,7 @@ App::post('/v1/account/sessions/anonymous')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => $expire
],
$detector->getOS(),
$detector->getClient(),
@@ -1641,7 +1641,6 @@ App::post('/v1/account/sessions/anonymous')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -1841,7 +1840,6 @@ App::get('/v1/account/sessions')
$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;
}
@@ -1948,7 +1946,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);
@@ -2450,8 +2447,6 @@ App::patch('/v1/account/sessions/:sessionId')
$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())
@@ -2505,7 +2500,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
+31
View File
@@ -693,6 +693,37 @@ App::patch('/v1/projects/:projectId/auth/duration')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/session-refresh')
->desc('Update project session refresh')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateSessionRefresh')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('sessionRefresh', false, new Boolean(), 'Automatic session refresh. If enabled, sessions are automatically extended to session duration on every request.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $sessionRefresh, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['sessionRefresh'] = $sessionRefresh;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/:method')
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
->groups(['api', 'projects'])
+29 -10
View File
@@ -154,8 +154,7 @@ App::init()
$response
->addHeader('X-RateLimit-Limit', $limit)
->addHeader('X-RateLimit-Remaining', $remaining)
->addHeader('X-RateLimit-Reset', $time)
;
->addHeader('X-RateLimit-Reset', $time);
}
$enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
@@ -170,6 +169,27 @@ App::init()
}
}
/*
* Session refresh
*/
if ($project->getAttribute('auths', [])['sessionRefresh'] ?? false) {
if ($user && !$user->isEmpty()) {
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$currentSessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration);
if ($currentSessionId) {
$currentSession = $dbForProject->getDocument('sessions', $currentSessionId);
if (!$currentSession->isEmpty()) {
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $authDuration));
$currentSession = $currentSession->setAttribute('expire', $expire);
$dbForProject->updateDocument('sessions', $currentSession->getId(), $currentSession);
$dbForProject->deleteCachedDocument('users', $user->getId());
}
}
}
}
/*
* Background Jobs
*/
@@ -240,7 +260,7 @@ App::init()
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -252,8 +272,7 @@ App::init()
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($data['contentType'])
->send(base64_decode($data['payload']))
;
->send(base64_decode($data['payload']));
} else {
$response->addHeader('X-Appwrite-Cache', 'miss');
}
@@ -495,7 +514,7 @@ App::shutdown()
'resource' => $resource,
'contentType' => $response->getContentType(),
'payload' => base64_encode($data['payload']),
]) ;
]);
$signature = md5($data);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
@@ -503,10 +522,10 @@ App::shutdown()
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
+1 -1
View File
@@ -363,7 +363,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::addSeconds(new \DateTime($session->getAttribute('expire')), $expires)) >= DateTime::formatTz(DateTime::now())
) {
return $session->getId();
}