aboutsummaryrefslogtreecommitdiff
path: root/Psso/Session.php
diff options
context:
space:
mode:
authorwinter Sparkles2026-08-09 02:26:33 +0100
committerwinter Sparkles2026-08-09 02:26:33 +0100
commit3354d45762bbe0db33e3d26d8997e6f6624f24d7 (patch)
tree4bef4bf59488f443ed54ff6e30002268836ac259 /Psso/Session.php
parent4bb659e1f7bb5850db5695a536428a9b71e3ffe5 (diff)
implement sessions and also log in/out
session data is stored in sqlite, not using php sessions
Diffstat (limited to 'Psso/Session.php')
-rw-r--r--Psso/Session.php101
1 files changed, 101 insertions, 0 deletions
diff --git a/Psso/Session.php b/Psso/Session.php
new file mode 100644
index 0000000..35a6228
--- /dev/null
+++ b/Psso/Session.php
@@ -0,0 +1,101 @@
+<?php
+namespace Psso;
+
+class Session {
+ protected static ?self $instance = null;
+ protected static ?array $pdoSetup = null;
+ protected static ?array $config = null;
+
+ protected \PDO $db;
+ private ?string $token = null;
+
+ public static function setDsn(
+ array $config,
+ string $dsn,
+ ?string $username = null,
+ #[\SensitiveParameter] ?string $password = null
+ ): void {
+ static::$config = $config;
+ static::$pdoSetup = [$dsn, $username, $password];
+ }
+
+ protected function __construct() {
+ $args = static::$pdoSetup;
+ $args[] = [\PDO::ATTR_PERSISTENT => true];
+ $this->db = new \PDO(...$args);
+ $this->setup();
+ }
+
+ public static function get(): self {
+ if (!isset(self::$instance)) {
+ self::$instance = new self();
+ }
+ self::$instance->setCookie();
+ return self::$instance;
+ }
+
+ protected function setup(): void {
+ $this->db->exec(
+ <<<'END'
+ create table if not exists session (
+ token text primary key not null,
+ identity text not null default 'N;',
+ challenges text not null default 'N;'
+ );
+ END
+ );
+ }
+
+ protected function domain(): string {
+ return explode('.', $_SERVER['HTTP_HOST'], 2)[1];
+ }
+
+ protected function setCookie(): void {
+ if (isset($_COOKIE['PSSO_session'])) return;
+ header(
+ 'Set-Cookie: PSSO_session=' . $this->currentToken()
+ . '; Domain=' . $this->domain()
+ . '; HttpOnly'
+ . '; Max-Age=' . static::$config['auth']['lifetime']
+ . '; Path=/'
+ . '; SameSite=Lax'
+ . '; Secure'
+ );
+ }
+
+ protected function currentToken(): string {
+ return $this->token = $this->token ??
+ $_COOKIE['PSSO_session'] ??
+ base64_encode(random_bytes(129));
+ }
+
+ protected function setColumn(string $column, mixed $data): void {
+ $stmt = $this->db->prepare(
+ "insert into session(token, $column) values (?, ?) "
+ . "on conflict do update set $column=excluded.$column"
+ );
+ $stmt->execute([$this->currentToken(), serialize($data)]);
+ }
+
+ public function getColumn(string $column): mixed {
+ $stmt = $this->db->prepare("select $column from session where token=?");
+ $stmt->execute([$this->currentToken()]);
+ return unserialize($stmt->fetchColumn(0));
+ }
+
+ public function setIdentity(mixed $identity): void {
+ $this->setColumn('identity', $identity);
+ }
+
+ public function getIdentity(): mixed {
+ return $this->getColumn('identity');
+ }
+
+ public function setChallenges(mixed $challenges): void {
+ $this->setColumn('challenges', $challenges);
+ }
+
+ public function getChallenges(): mixed {
+ return $this->getColumn('challenges');
+ }
+}