To generate and validate a cookie, you can utilize the following code snippet:
define('COOKIE_TOKEN', 'my_token');
class BaseAuth
{
protected $uid;
private static function base64url_encode(string $s): string
{
return strtr($s,'+/=','-|_');
}
private static function base64url_decode(string $s): string
{
return strtr($s,'-|_','+/=');
}
// Ensuring encrypted characters are URL-safe by encoding after encryption
protected function token_encode(String $string): string
{
$iv_size = openssl_cipher_iv_length(TYPE_CRYPT);
$iv = openssl_random_pseudo_bytes($iv_size);
$encrypted_string = @openssl_encrypt($string, TYPE_CRYPT, SALT, 0, $iv);
// Need IV for decoding, so return IV + encrypted string
return self::base64url_encode($encrypted_string).'!'.self::base64url_encode(base64_encode($iv));
}
// Decoding from URL-safe before decryption
protected function token_decode(String $string): string
{
list($encrypted_string, $iv) = explode('!', $string);
$string = @openssl_decrypt(self::base64url_decode($encrypted_string), TYPE_CRYPT, SALT, 0, base64_decode(self::base64url_decode($iv)));
return $string;
}
// Logging out the user
public function clear_cookie()
{
setcookie(COOKIE_TOKEN, '', time() - 300, '/api', '', FALSE, TRUE); // non-secure; HTTP-only
}
private function userIP(): string
{
return $_SERVER['REMOTE_ADDR'];
}
// Validating login token
public function authorized(): bool
{
if(isset($_COOKIE[COOKIE_TOKEN]))
{
$stamp = time();
$text = $this->token_decode($_COOKIE[COOKIE_TOKEN]);
if($text != '')
{
$json = json_decode($text,TRUE);
if(json_last_error() == JSON_ERROR_NONE)
{
if($json['at'] <= $stamp AND $json['exp'] > $stamp AND $json['ip'] == $this->userIP() AND $json['id'] != 0)
{
$res = $db->query("SELECT id,active,last_update,last_update > '".$json['last']."'::timestamptz AS expired FROM account WHERE id = ".$json['id']);
$info = $db->fetch_assoc($res);
if($info['active'] != 0)
{
if($info['expired'] == 0)
{
$this->sendToken($info);
$this->uid = $json['id'];
return TRUE;
}
}
}
}
}
$this->clear_cookie();
}
return FALSE;
}
public function login(String $username, String $password): bool
{
$stm = $db-prepare("SELECT id,user_name AS username,user_pass,full_name,active,last_update,COALESCE(blocked_until,NOW()) > NOW() AS blocked
FROM account WHERE user_name = :user");
$res = $stm->execute(array('user' => strtolower($json['username'])));
if($res->rowCount())
{
$info = $db->fetch_assoc($res);
if($info['active'] == 0)
{
return FALSE;
}
elseif($info['blocked'] != 0)
{
$db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
return FALSE;
}
elseif(!password_verify($password, $info['user_pass']))
{
$db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
return FALSE;
}
else
{
unset($info['user_pass']);
unset($info['blocked']);
$this->sendToken($info);
return TRUE;
}
}
}
}
If authentication and authorization are not required, and you just need unpredictable image IDs, UUIDs can be used.