use new session verify

This commit is contained in:
Damodar Lohani
2025-11-04 03:48:57 +00:00
parent 617c1aaee3
commit 2ae474971d
4 changed files with 512 additions and 194 deletions
File diff suppressed because it is too large Load Diff
+50
View File
@@ -294,3 +294,53 @@ const TOKENS_RESOURCE_TYPE_DATABASES = 'databases';
const SCHEDULE_RESOURCE_TYPE_EXECUTION = 'execution';
const SCHEDULE_RESOURCE_TYPE_FUNCTION = 'function';
const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message';
// Auth
/**
* Token Types.
*/
const TOKEN_TYPE_LOGIN = 1; // Deprecated
const TOKEN_TYPE_VERIFICATION = 2;
const TOKEN_TYPE_RECOVERY = 3;
const TOKEN_TYPE_INVITE = 4;
const TOKEN_TYPE_MAGIC_URL = 5;
const TOKEN_TYPE_PHONE = 6;
const TOKEN_TYPE_OAUTH2 = 7;
const TOKEN_TYPE_GENERIC = 8;
const TOKEN_TYPE_EMAIL = 9; // OTP
/**
* Session Providers.
*/
const SESSION_PROVIDER_EMAIL = 'email';
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
const SESSION_PROVIDER_PHONE = 'phone';
const SESSION_PROVIDER_OAUTH2 = 'oauth2';
const SESSION_PROVIDER_TOKEN = 'token';
const SESSION_PROVIDER_SERVER = 'server';
/**
* Token Expiration times.
*/
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
/**
* Token Lengths.
*/
const TOKEN_LENGTH_MAGIC_URL = 64;
const TOKEN_LENGTH_VERIFICATION = 256;
const TOKEN_LENGTH_RECOVERY = 256;
const TOKEN_LENGTH_OAUTH2 = 64;
const TOKEN_LENGTH_SESSION = 256;
/**
* MFA
*/
const MFA_RECENT_DURATION = 1800; // 30 mins
+4
View File
@@ -23,6 +23,7 @@ use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Platform;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Database\Documents\User;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Executor\Executor;
@@ -440,6 +441,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$database->setDocumentType('users', User::class);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -469,6 +471,8 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$database->setDocumentType('users', User::class);
return $database;
}, ['pools', 'cache']);
@@ -0,0 +1,181 @@
<?php
namespace Appwrite\Utopia\Database\Documents;
use Utopia\Auth\Proof;
use Utopia\Auth\Proofs\Token;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Roles;
class User extends Document
{
public const ROLE_ANY = 'any';
public const ROLE_GUESTS = 'guests';
public const ROLE_USERS = 'users';
public const ROLE_ADMIN = 'admin';
public const ROLE_DEVELOPER = 'developer';
public const ROLE_OWNER = 'owner';
public const ROLE_APPS = 'apps';
public const ROLE_SYSTEM = 'system';
public function getEmail(): ?string
{
return $this->getAttribute('email');
}
public function getPhone(): ?string
{
return $this->getAttribute('phone');
}
/**
* Returns all roles for a user.
*
* @return array<string>
*/
public function getRoles(): array
{
$roles = [];
if (!$this->isPrivileged(Authorization::getRoles()) && !$this->isApp(Authorization::getRoles())) {
if ($this->getId()) {
$roles[] = Role::user($this->getId())->toString();
$roles[] = Role::users()->toString();
$emailVerified = $this->getAttribute('emailVerification', false);
$phoneVerified = $this->getAttribute('phoneVerification', false);
if ($emailVerified || $phoneVerified) {
$roles[] = Role::user($this->getId(), Roles::DIMENSION_VERIFIED)->toString();
$roles[] = Role::users(Roles::DIMENSION_VERIFIED)->toString();
} else {
$roles[] = Role::user($this->getId(), Roles::DIMENSION_UNVERIFIED)->toString();
$roles[] = Role::users(Roles::DIMENSION_UNVERIFIED)->toString();
}
} else {
return [Role::guests()->toString()];
}
}
foreach ($this->getAttribute('memberships', []) as $node) {
if (!isset($node['confirm']) || !$node['confirm']) {
continue;
}
if (isset($node['$id']) && isset($node['teamId'])) {
$roles[] = Role::team($node['teamId'])->toString();
$roles[] = Role::member($node['$id'])->toString();
if (isset($node['roles'])) {
foreach ($node['roles'] as $nodeRole) { // Set all team roles
$roles[] = Role::team($node['teamId'], $nodeRole)->toString();
}
}
}
}
foreach ($this->getAttribute('labels', []) as $label) {
$roles[] = 'label:' . $label;
}
return $roles;
}
/**
* Check if user is anonymous.
*
* @param Document $this
* @return bool
*/
public function isAnonymous(): bool
{
return is_null($this->getEmail())
&& is_null($this->getPhone());
}
/**
* Is Privileged User?
*
* @param array<string> $roles
*
* @return bool
*/
public function isPrivileged(array $roles): bool
{
if (
in_array(self::ROLE_OWNER, $roles) ||
in_array(self::ROLE_DEVELOPER, $roles) ||
in_array(self::ROLE_ADMIN, $roles)
) {
return true;
}
return false;
}
/**
* Is App User?
*
* @param array<string> $roles
*
* @return bool
*/
public function isApp(array $roles): bool
{
if (in_array(self::ROLE_APPS, $roles)) {
return true;
}
return false;
}
public function tokenVerify(int $type = null, string $secret, Proof $proofForToken): false|Document
{
$tokens = $this->getAttribute('tokens', []);
foreach ($tokens as $token) {
if (
$token->isSet('secret') &&
$token->isSet('expire') &&
$token->isSet('type') &&
($type === null || $token->getAttribute('type') === $type) &&
$proofForToken->verify($secret, $token->getAttribute('secret')) &&
DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
) {
return $token;
}
}
return false;
}
/**
* Verify session and check that its not expired.
*
* @param array<Document> $sessions
* @param string $secret
*
* @return bool|string
*/
public function sessionVerify(string $secret, Token $proofForToken)
{
$sessions = $this->getAttribute('sessions', []);
foreach ($sessions as $session) {
if (
$session->isSet('secret') &&
$session->isSet('provider') &&
$proofForToken->verify($secret, $session->getAttribute('secret')) &&
DateTime::formatTz(DateTime::format(new \DateTime($session->getAttribute('expire')))) >= DateTime::formatTz(DateTime::now())
) {
return $session->getId();
}
}
return false;
return false;
}
}