Refactor remove moved to utopia auth

This commit is contained in:
Damodar Lohani 2025-11-04 05:37:45 +00:00
parent df1f14c853
commit 897f512b47
10 changed files with 41 additions and 994 deletions

View file

@ -2,13 +2,9 @@
namespace Appwrite\Auth;
use Appwrite\Auth\Hash\Argon2;
use Appwrite\Auth\Hash\Bcrypt;
use Appwrite\Auth\Hash\Md5;
use Appwrite\Auth\Hash\Phpass;
use Appwrite\Auth\Hash\Scrypt;
use Appwrite\Auth\Hash\Scryptmodified;
use Appwrite\Auth\Hash\Sha;
use Appwrite\Utopia\Database\Documents\User;
use Utopia\Auth\Proof;
use Utopia\Auth\Proofs\Token;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
@ -17,186 +13,45 @@ use Utopia\Database\Validator\Roles;
class Auth
{
public const SUPPORTED_ALGOS = [
'argon2',
'bcrypt',
'md5',
'sha',
'phpass',
'scrypt',
'scryptMod',
'plaintext'
];
public const DEFAULT_ALGO = 'argon2';
public const DEFAULT_ALGO_OPTIONS = ['type' => 'argon2', 'memoryCost' => 2048, 'timeCost' => 4, 'threads' => 3];
/**
* User Roles.
*/
public const USER_ROLE_ANY = 'any';
public const USER_ROLE_GUESTS = 'guests';
public const USER_ROLE_USERS = 'users';
public const USER_ROLE_ADMIN = 'admin';
public const USER_ROLE_DEVELOPER = 'developer';
public const USER_ROLE_OWNER = 'owner';
public const USER_ROLE_APPS = 'apps';
public const USER_ROLE_SYSTEM = 'system';
/**
* Activity associated with user or the app.
*/
public const ACTIVITY_TYPE_APP = 'app';
public const ACTIVITY_TYPE_USER = 'user';
public const ACTIVITY_TYPE_GUEST = 'guest';
/**
* Token Types.
*/
public const TOKEN_TYPE_LOGIN = 1; // Deprecated
public const TOKEN_TYPE_VERIFICATION = 2;
public const TOKEN_TYPE_RECOVERY = 3;
public const TOKEN_TYPE_INVITE = 4;
public const TOKEN_TYPE_MAGIC_URL = 5;
public const TOKEN_TYPE_PHONE = 6;
public const TOKEN_TYPE_OAUTH2 = 7;
public const TOKEN_TYPE_GENERIC = 8;
public const TOKEN_TYPE_EMAIL = 9; // OTP
/**
* Session Providers.
*/
public const SESSION_PROVIDER_EMAIL = 'email';
public const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
public const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
public const SESSION_PROVIDER_PHONE = 'phone';
public const SESSION_PROVIDER_OAUTH2 = 'oauth2';
public const SESSION_PROVIDER_TOKEN = 'token';
public const SESSION_PROVIDER_SERVER = 'server';
/**
* Token Expiration times.
*/
public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
public const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
public const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
public const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
/**
* Token Lengths.
*/
public const TOKEN_LENGTH_MAGIC_URL = 64;
public const TOKEN_LENGTH_VERIFICATION = 256;
public const TOKEN_LENGTH_RECOVERY = 256;
public const TOKEN_LENGTH_OAUTH2 = 64;
public const TOKEN_LENGTH_SESSION = 256;
/**
* MFA
*/
public const MFA_RECENT_DURATION = 1800; // 30 mins
/**
* @var string
*/
public static $cookieName = 'a_session';
/**
* @var string
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
*/
public static $cookieNamePreview = 'a_jwt_console';
/**
* User Unique ID.
*
* @var string
*/
public static $unique = '';
/**
* User Secret Key.
*
* @var string
*/
public static $secret = '';
/**
* Set Cookie Name.
*
* @param $string
*
* @return string
*/
public static function setCookieName($string)
{
return self::$cookieName = $string;
}
/**
* Encode Session.
*
* @param string $id
* @param string $secret
*
* @return string
*/
public static function encodeSession($id, $secret)
{
return \base64_encode(\json_encode([
'id' => $id,
'secret' => $secret,
]));
}
/**
* Token type to session provider mapping.
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param int $type
*
* @return string
*/
public static function getSessionProviderByTokenType(int $type): string
{
switch ($type) {
case Auth::TOKEN_TYPE_VERIFICATION:
case Auth::TOKEN_TYPE_RECOVERY:
case Auth::TOKEN_TYPE_INVITE:
return Auth::SESSION_PROVIDER_EMAIL;
case Auth::TOKEN_TYPE_MAGIC_URL:
return Auth::SESSION_PROVIDER_MAGIC_URL;
case Auth::TOKEN_TYPE_PHONE:
return Auth::SESSION_PROVIDER_PHONE;
case Auth::TOKEN_TYPE_OAUTH2:
return Auth::SESSION_PROVIDER_OAUTH2;
case TOKEN_TYPE_VERIFICATION:
case TOKEN_TYPE_RECOVERY:
case TOKEN_TYPE_INVITE:
return SESSION_PROVIDER_EMAIL;
case TOKEN_TYPE_MAGIC_URL:
return SESSION_PROVIDER_MAGIC_URL;
case TOKEN_TYPE_PHONE:
return SESSION_PROVIDER_PHONE;
case TOKEN_TYPE_OAUTH2:
return SESSION_PROVIDER_OAUTH2;
default:
return Auth::SESSION_PROVIDER_TOKEN;
return SESSION_PROVIDER_TOKEN;
}
}
/**
* Decode Session.
*
* @param string $session
*
* @return array
*
* @throws \Exception
*/
public static function decodeSession($session)
{
$session = \json_decode(\base64_decode($session), true);
$default = ['id' => null, 'secret' => ''];
if (!\is_array($session)) {
return $default;
}
return \array_merge($default, $session);
}
/**
* Encode.
*
* One-way encryption
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param $string
*
* @return string
@ -206,124 +61,12 @@ class Auth
return \hash('sha256', $string);
}
/**
* Password Hash.
*
* One way string hashing for user passwords
*
* @param string $string
* @param string $algo hashing algorithm to use
* @param array $options algo-specific options
*
* @return bool|string|null
*/
public static function passwordHash(string $string, string $algo, array $options = [])
{
// Plain text not supported, just an alias. Switch to recommended algo
if ($algo === 'plaintext') {
$algo = Auth::DEFAULT_ALGO;
$options = Auth::DEFAULT_ALGO_OPTIONS;
}
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
}
switch ($algo) {
case 'argon2':
$hasher = new Argon2($options);
return $hasher->hash($string);
case 'bcrypt':
$hasher = new Bcrypt($options);
return $hasher->hash($string);
case 'md5':
$hasher = new Md5($options);
return $hasher->hash($string);
case 'sha':
$hasher = new Sha($options);
return $hasher->hash($string);
case 'phpass':
$hasher = new Phpass($options);
return $hasher->hash($string);
case 'scrypt':
$hasher = new Scrypt($options);
return $hasher->hash($string);
case 'scryptMod':
$hasher = new Scryptmodified($options);
return $hasher->hash($string);
default:
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
}
}
/**
* Password verify.
*
* @param string $plain
* @param string $hash
* @param string $algo hashing algorithm used to hash
* @param array $options algo-specific options
*
* @return bool
*/
public static function passwordVerify(string $plain, string $hash, string $algo, array $options = [])
{
// Plain text not supported, just an alias. Switch to recommended algo
if ($algo === 'plaintext') {
$algo = Auth::DEFAULT_ALGO;
$options = Auth::DEFAULT_ALGO_OPTIONS;
}
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
}
switch ($algo) {
case 'argon2':
$hasher = new Argon2($options);
return $hasher->verify($plain, $hash);
case 'bcrypt':
$hasher = new Bcrypt($options);
return $hasher->verify($plain, $hash);
case 'md5':
$hasher = new Md5($options);
return $hasher->verify($plain, $hash);
case 'sha':
$hasher = new Sha($options);
return $hasher->verify($plain, $hash);
case 'phpass':
$hasher = new Phpass($options);
return $hasher->verify($plain, $hash);
case 'scrypt':
$hasher = new Scrypt($options);
return $hasher->verify($plain, $hash);
case 'scryptMod':
$hasher = new Scryptmodified($options);
return $hasher->verify($plain, $hash);
default:
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
}
}
/**
* Password Generator.
*
* Generate random password string
*
* @param int $length
*
* @return string
*/
public static function passwordGenerator(int $length = 20): string
{
return \bin2hex(\random_bytes($length));
}
/**
* Token Generator.
*
* Generate random password string
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param int $length Length of returned token
*
* @return string
@ -340,36 +83,17 @@ class Auth
return substr($token, 0, $length);
}
/**
* Code Generator.
*
* Generate random code string
*
* @param int $length
*
* @return string
*/
public static function codeGenerator(int $length = 6): string
{
$value = '';
for ($i = 0; $i < $length; $i++) {
$value .= random_int(0, 9);
}
return $value;
}
/**
* Verify token and check that its not expired.
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param array<Document> $tokens
* @param int $type Type of token to verify, if null will verify any type
* @param string $secret
*
* @return false|Document
*/
public static function tokenVerify(array $tokens, int $type = null, string $secret): false|Document
public static function tokenVerify(array $tokens, int $type = null, string $secret, Proof $proofForToken): false|Document
{
foreach ($tokens as $token) {
if (
@ -377,7 +101,7 @@ class Auth
$token->isSet('expire') &&
$token->isSet('type') &&
($type === null || $token->getAttribute('type') === $type) &&
$token->getAttribute('secret') === self::hash($secret) &&
$proofForToken->verify($secret, $token->getAttribute('secret')) &&
DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
) {
return $token;
@ -390,18 +114,19 @@ class Auth
/**
* Verify session and check that its not expired.
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param array<Document> $sessions
* @param string $secret
*
* @return bool|string
*/
public static function sessionVerify(array $sessions, string $secret)
public static function sessionVerify(array $sessions, string $secret, Token $proofForToken)
{
foreach ($sessions as $session) {
if (
$session->isSet('secret') &&
$session->isSet('provider') &&
$session->getAttribute('secret') === self::hash($secret) &&
$proofForToken->verify($secret, $session->getAttribute('secret')) &&
DateTime::formatTz(DateTime::format(new \DateTime($session->getAttribute('expire')))) >= DateTime::formatTz(DateTime::now())
) {
return $session->getId();
@ -414,6 +139,7 @@ class Auth
/**
* Is Privileged User?
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param array<string> $roles
*
* @return bool
@ -421,9 +147,9 @@ class Auth
public static function isPrivilegedUser(array $roles): bool
{
if (
in_array(self::USER_ROLE_OWNER, $roles) ||
in_array(self::USER_ROLE_DEVELOPER, $roles) ||
in_array(self::USER_ROLE_ADMIN, $roles)
in_array(User::ROLE_OWNER, $roles) ||
in_array(User::ROLE_DEVELOPER, $roles) ||
in_array(User::ROLE_ADMIN, $roles)
) {
return true;
}
@ -434,13 +160,14 @@ class Auth
/**
* Is App User?
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param array<string> $roles
*
* @return bool
*/
public static function isAppUser(array $roles): bool
{
if (in_array(self::USER_ROLE_APPS, $roles)) {
if (in_array(User::ROLE_APPS, $roles)) {
return true;
}
@ -450,6 +177,7 @@ class Auth
/**
* Returns all roles for a user.
*
* @deprecated We plan to deprecate this class in the future. Use Utopia Auth when possible.
* @param Document $user
* @return array<string>
*/
@ -500,16 +228,4 @@ class Auth
return $roles;
}
/**
* Check if user is anonymous.
*
* @param Document $user
* @return bool
*/
public static function isAnonymousUser(Document $user): bool
{
return is_null($user->getAttribute('email'))
&& is_null($user->getAttribute('phone'));
}
}

View file

@ -1,62 +0,0 @@
<?php
namespace Appwrite\Auth;
abstract class Hash
{
/**
* @var array $options Hashing-algo specific options
*/
protected array $options = [];
/**
* @param array $options Hashing-algo specific options
*/
public function __construct(array $options = [])
{
$this->setOptions($options);
}
/**
* Set hashing algo options
*
* @param array $options Hashing-algo specific options
*/
public function setOptions(array $options): self
{
$this->options = \array_merge([], $this->getDefaultOptions(), $options);
return $this;
}
/**
* Get hashing algo options
*
* @return array $options Hashing-algo specific options
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @param string $password Input password to hash
*
* @return string hash
*/
abstract public function hash(string $password): string;
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
abstract public function verify(string $password, string $hash): bool;
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
abstract public function getDefaultOptions(): array;
}

View file

@ -1,47 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* Argon2 accepted options:
* int threads
* int time_cost
* int memory_cost
*
* Reference: https://www.php.net/manual/en/function.password-hash.php#example-983
*/
class Argon2 extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
return \password_hash($password, PASSWORD_ARGON2ID, $this->getOptions());
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return \password_verify($password, $hash);
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 3];
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* Bcrypt accepted options:
* int cost
* string? salt; auto-generated if empty
*
* Reference: https://www.php.net/manual/en/password.constants.php
*/
class Bcrypt extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
return \password_hash($password, PASSWORD_BCRYPT, $this->getOptions());
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return \password_verify($password, $hash);
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return [ 'cost' => 8 ];
}
}

View file

@ -1,44 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* MD5 does not accept any options.
*
* Reference: https://www.php.net/manual/en/function.md5.php
*/
class Md5 extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
return \md5($password);
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return $this->hash($password) === $hash;
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return [];
}
}

View file

@ -1,290 +0,0 @@
<?php
/**
* Portable PHP password hashing framework.
* source Version 0.5 / genuine.
* Written by Solar Designer <solar at openwall.com> in 2004-2017 and placed in
* the public domain. Revised in subsequent years, still public domain.
* There's absolutely no warranty.
* The homepage URL for the source framework is: http://www.openwall.com/phpass/
* Please be sure to update the Version line if you edit this file in any way.
* It is suggested that you leave the main version number intact, but indicate
* your project name (after the slash) and add your own revision information.
* Please do not change the "private" password hashing method implemented in
* here, thereby making your hashes incompatible. However, if you must, please
* change the hash type identifier (the "$P$") to something different.
* Obviously, since this code is in the public domain, the above are not
* requirements (there can be none), but merely suggestions.
*
* @author Solar Designer <solar@openwall.com>
* @copyright Copyright (C) 2017 All rights reserved.
* @license http://www.opensource.org/licenses/mit-license.html MIT License; see LICENSE.txt
*/
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* PHPass accepted options:
* int iteration_count_log2; The Logarithmic cost value used when generating hash values indicating the number of rounds used to generate hashes
* string portable_hashes
* string random_state; The cached random state
*
* Reference: https://github.com/photodude/phpass
*/
class Phpass extends Hash
{
/**
* Alphabet used in itoa64 conversions.
*
* @var string
* @since 0.1.0
*/
protected string $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
$randomState = \microtime();
if (\function_exists('getmypid')) {
$randomState .= getmypid();
}
return ['iteration_count_log2' => 8, 'portable_hashes' => false, 'random_state' => $randomState];
}
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
$options = $this->getDefaultOptions();
$random = '';
if (CRYPT_BLOWFISH === 1 && !$options['portable_hashes']) {
$random = $this->getRandomBytes(16, $options);
$hash = crypt($password, $this->gensaltBlowfish($random, $options));
if (strlen($hash) === 60) {
return $hash;
}
}
if (strlen($random) < 6) {
$random = $this->getRandomBytes(6, $options);
}
$hash = $this->cryptPrivate($password, $this->gensaltPrivate($random, $options));
if (strlen($hash) === 34) {
return $hash;
}
/**
* Returning '*' on error is safe here, but would _not_ be safe
* in a crypt(3)-like function used _both_ for generating new
* hashes and for validating passwords against existing hashes.
*/
return '*';
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
$verificationHash = $this->cryptPrivate($password, $hash);
if ($verificationHash[0] === '*') {
$verificationHash = crypt($password, $hash);
}
/**
* This is not constant-time. In order to keep the code simple,
* for timing safety we currently rely on the salts being
* unpredictable, which they are at least in the non-fallback
* cases (that is, when we use /dev/urandom and bcrypt).
*/
return $hash === $verificationHash;
}
/**
* @param int $count
*
* @return String $output
* @since 0.1.0
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
*/
protected function getRandomBytes(int $count, array $options): string
{
if (!is_int($count) || $count < 1) {
throw new \Exception('Argument count must be a positive integer');
}
$output = '';
if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);
fclose($fh);
}
if (strlen($output) < $count) {
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$options['iteration_count_log2'] = md5(microtime() . $options['iteration_count_log2']);
$output .= md5($options['iteration_count_log2'], true);
}
$output = substr($output, 0, $count);
}
return $output;
}
/**
* @param String $input
* @param int $count
*
* @return String $output
* @since 0.1.0
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
*/
protected function encode64($input, $count)
{
if (!is_int($count) || $count < 1) {
throw new \Exception('Argument count must be a positive integer');
}
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
if ($i < $count) {
$value |= ord($input[$i]) << 8;
}
$output .= $this->itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count) {
break;
}
if ($i < $count) {
$value |= ord($input[$i]) << 16;
}
$output .= $this->itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count) {
break;
}
$output .= $this->itoa64[($value >> 18) & 0x3f];
} while ($i < $count);
return $output;
}
/**
* @param String $input
*
* @return String $output
* @since 0.1.0
*/
private function gensaltPrivate($input, $options)
{
$output = '$P$';
$output .= $this->itoa64[min($options['iteration_count_log2'] + ((PHP_VERSION >= '5') ? 5 : 3), 30)];
$output .= $this->encode64($input, 6);
return $output;
}
/**
* @param String $password
* @param String $setting
*
* @return String $output
* @since 0.1.0
*/
private function cryptPrivate($password, $setting)
{
$output = '*0';
if (substr($setting, 0, 2) === $output) {
$output = '*1';
}
$id = substr($setting, 0, 3);
// We use "$P$", phpBB3 uses "$H$" for the same thing
if ($id !== '$P$' && $id !== '$H$') {
return $output;
}
$count_log2 = strpos($this->itoa64, $setting[3]);
if ($count_log2 < 7 || $count_log2 > 30) {
return $output;
}
$count = 1 << $count_log2;
$salt = substr($setting, 4, 8);
if (strlen($salt) !== 8) {
return $output;
}
/**
* We were kind of forced to use MD5 here since it's the only
* cryptographic primitive that was available in all versions of PHP
* in use. To implement our own low-level crypto in PHP
* would have result in much worse performance and
* consequently in lower iteration counts and hashes that are
* quicker to crack (by non-PHP code).
*/
$hash = md5($salt . $password, true);
do {
$hash = md5($hash . $password, true);
} while (--$count);
$output = substr($setting, 0, 12);
$output .= $this->encode64($hash, 16);
return $output;
}
/**
* @param String $input
*
* @return String $output
* @since 0.1.0
*/
private function gensaltBlowfish($input, $options)
{
/**
* This one needs to use a different order of characters and a
* different encoding scheme from the one in encode64() above.
* We care because the last character in our encoded string will
* only represent 2 bits. While two known implementations of
* bcrypt will happily accept and correct a salt string which
* has the 4 unused bits set to non-zero, we do not want to take
* chances and we also do not want to waste an additional byte
* of entropy.
*/
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$output = '$2a$';
$output .= chr(ord('0') + intval($options['iteration_count_log2'] / 10));
$output .= chr(ord('0') + $options['iteration_count_log2'] % 10);
$output .= '$';
$i = 0;
do {
$c1 = ord($input[$i++]);
$output .= $itoa64[$c1 >> 2];
$c1 = ($c1 & 0x03) << 4;
if ($i >= 16) {
$output .= $itoa64[$c1];
break;
}
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 4;
$output .= $itoa64[$c1];
$c1 = ($c2 & 0x0f) << 2;
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 6;
$output .= $itoa64[$c1];
$output .= $itoa64[$c2 & 0x3f];
} while (1);
return $output;
}
}

View file

@ -1,51 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* Scrypt accepted options:
* string? salt; auto-generated if empty
* int costCpu
* int costMemory
* int costParallel
* int length
*
* Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116
*/
class Scrypt extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
$options = $this->getOptions();
return \scrypt($password, $options['salt'], $options['costCpu'], $options['costMemory'], $options['costParallel'], $options['length']);
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return $hash === $this->hash($password);
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return [ 'costCpu' => 8, 'costMemory' => 14, 'costParallel' => 1, 'length' => 64 ];
}
}

View file

@ -1,80 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* This is Scrypt hash with some additional steps added by Google.
*
* string salt
* string saltSeparator
* strin signerKey
*
* Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116
*/
class Scryptmodified extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
$options = $this->getOptions();
$derivedKeyBytes = $this->generateDerivedKey($password);
$signerKeyBytes = \base64_decode($options['signerKey']);
$hashedPassword = $this->hashKeys($signerKeyBytes, $derivedKeyBytes);
return \base64_encode($hashedPassword);
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return $this->hash($password) === $hash;
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return [ ];
}
private function generateDerivedKey(string $password)
{
$options = $this->getOptions();
$saltBytes = \base64_decode($options['salt']);
$saltSeparatorBytes = \base64_decode($options['saltSeparator']);
$password = mb_convert_encoding($password, 'UTF-8');
$derivedKey = \scrypt($password, $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64);
$derivedKey = \hex2bin($derivedKey);
return $derivedKey;
}
private function hashKeys($signerKeyBytes, $derivedKeyBytes): string
{
$key = \substr($derivedKeyBytes, 0, 32);
$iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
$hash = \openssl_encrypt($signerKeyBytes, 'aes-256-ctr', $key, OPENSSL_RAW_DATA, $iv);
return $hash;
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace Appwrite\Auth\Hash;
use Appwrite\Auth\Hash;
/*
* SHA accepted options:
* string? version. Allowed:
* - Version 1: sha1
* - Version 2: sha224, sha256, sha384, sha512/224, sha512/256, sha512
* - Version 3: sha3-224, sha3-256, sha3-384, sha3-512
*
* Reference: https://www.php.net/manual/en/function.hash-algos.php
*/
class Sha extends Hash
{
/**
* @param string $password Input password to hash
*
* @return string hash
*/
public function hash(string $password): string
{
$algo = $this->getOptions()['version'];
return \hash($algo, $password);
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
return $this->hash($password) === $hash;
}
/**
* Get default options for specific hashing algo
*
* @return array options named array
*/
public function getDefaultOptions(): array
{
return [ 'version' => 'sha3-512' ];
}
}

View file

@ -5,6 +5,7 @@ namespace Appwrite\Auth;
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Documents\User;
use Utopia\Config\Config;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
@ -110,16 +111,16 @@ class Key
$secret = $key;
}
$role = Auth::USER_ROLE_APPS;
$role = User::ROLE_APPS;
$roles = Config::getParam('roles', []);
$scopes = $roles[Auth::USER_ROLE_APPS]['scopes'] ?? [];
$scopes = $roles[User::ROLE_APPS]['scopes'] ?? [];
$expired = false;
$guestKey = new Key(
$project->getId(),
$type,
Auth::USER_ROLE_GUESTS,
$roles[Auth::USER_ROLE_GUESTS]['scopes'] ?? [],
User::ROLE_GUESTS,
$roles[User::ROLE_GUESTS]['scopes'] ?? [],
'UNKNOWN'
);