mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Implement session extension
This commit is contained in:
@@ -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' => [
|
||||
[
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user