Add passkey and magic link authentication

This commit is contained in:
JB
2026-07-25 01:54:33 -04:00
parent 3c9f443499
commit c3244b0c79
31 changed files with 1017 additions and 58 deletions
+45 -2
View File
@@ -1,5 +1,5 @@
-- Initial embedded Turso schema. Every statement is safe to run repeatedly.
CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, password_hash TEXT, discord_id TEXT UNIQUE, role TEXT NOT NULL DEFAULT 'user', created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, login_username TEXT, password_hash TEXT, discord_id TEXT UNIQUE, role TEXT NOT NULL DEFAULT 'user', created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS user_profiles (
user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
display_name TEXT NOT NULL,
@@ -40,10 +40,52 @@ CREATE TABLE IF NOT EXISTS user_status (
CREATE TABLE IF NOT EXISTS login_events (
id TEXT PRIMARY KEY,
user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
provider TEXT NOT NULL CHECK(provider IN ('password', 'discord')),
provider TEXT NOT NULL CHECK(provider IN ('password', 'discord', 'magic_link', 'passkey')),
succeeded INTEGER NOT NULL CHECK(succeeded IN (0, 1)),
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS user_emails (
user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
email TEXT NOT NULL COLLATE NOCASE UNIQUE,
verified_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS magic_link_tokens (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
email TEXT NOT NULL COLLATE NOCASE,
purpose TEXT NOT NULL CHECK(purpose IN ('login', 'verify_email')),
token_hash TEXT NOT NULL UNIQUE,
expires_at TEXT NOT NULL,
consumed_at TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS magic_link_tokens_expiry_idx ON magic_link_tokens(expires_at);
CREATE TABLE IF NOT EXISTS passkeys (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
webauthn_user_id TEXT NOT NULL,
public_key BLOB NOT NULL,
counter INTEGER NOT NULL DEFAULT 0,
device_type TEXT NOT NULL,
backed_up INTEGER NOT NULL DEFAULT 0 CHECK(backed_up IN (0, 1)),
transports TEXT,
name TEXT NOT NULL,
last_used_at TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS passkeys_user_idx ON passkeys(user_id);
CREATE TABLE IF NOT EXISTS webauthn_challenges (
id TEXT PRIMARY KEY,
user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
flow TEXT NOT NULL CHECK(flow IN ('registration', 'authentication')),
challenge TEXT NOT NULL,
rp_id TEXT NOT NULL,
origin TEXT NOT NULL,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS webauthn_challenges_expiry_idx ON webauthn_challenges(expires_at);
CREATE TABLE IF NOT EXISTS deleted_accounts (
user_id TEXT PRIMARY KEY REFERENCES users(id),
original_username TEXT NOT NULL,
@@ -85,6 +127,7 @@ CREATE TABLE IF NOT EXISTS discord_profiles (
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS users_username_nocase_idx ON users(username COLLATE NOCASE);
CREATE UNIQUE INDEX IF NOT EXISTS users_login_username_nocase_idx ON users(login_username COLLATE NOCASE) WHERE login_username IS NOT NULL;
CREATE TABLE IF NOT EXISTS stat_attributes (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,