-- Accounts, devices, tokens and account data: the userapi tables. -- -- Timestamps are milliseconds since the Unix epoch, the unit the -- Client-Server API uses on the wire, so values go in and out unconverted -- and both SQL drivers store the same numbers. -- -- Tokens are stored as SHA-256 hashes, never in the clear: they are bearer -- credentials that by default never expire, and one hash per request is -- cheap next to what a leaked database dump would otherwise hand over. CREATE TABLE IF NOT EXISTS user_accounts ( localpart TEXT NOT NULL PRIMARY KEY, -- An empty hash means the account has no password and cannot use -- m.login.password; it is not a hash that anything can match. password_hash TEXT NOT NULL DEFAULT '', display_name TEXT NOT NULL DEFAULT '', avatar_url TEXT NOT NULL DEFAULT '', admin BOOLEAN NOT NULL DEFAULT FALSE, deactivated BOOLEAN NOT NULL DEFAULT FALSE, created_at_ms BIGINT NOT NULL ); CREATE TABLE IF NOT EXISTS user_devices ( localpart TEXT NOT NULL REFERENCES user_accounts (localpart) ON DELETE CASCADE, device_id TEXT NOT NULL, display_name TEXT NOT NULL DEFAULT '', created_at_ms BIGINT NOT NULL, last_seen_ms BIGINT NOT NULL DEFAULT 0, last_seen_ip TEXT NOT NULL DEFAULT '', user_agent TEXT NOT NULL DEFAULT '', PRIMARY KEY (localpart, device_id) ); CREATE TABLE IF NOT EXISTS user_access_tokens ( token_hash TEXT NOT NULL PRIMARY KEY, localpart TEXT NOT NULL, device_id TEXT NOT NULL, created_at_ms BIGINT NOT NULL, -- 0 means the token never expires. expires_at_ms BIGINT NOT NULL DEFAULT 0, FOREIGN KEY (localpart, device_id) REFERENCES user_devices (localpart, device_id) ON DELETE CASCADE ); -- Revoking a device deletes its tokens by this index, not by table scan. CREATE INDEX IF NOT EXISTS user_access_tokens_device_idx ON user_access_tokens (localpart, device_id); CREATE TABLE IF NOT EXISTS user_refresh_tokens ( token_hash TEXT NOT NULL PRIMARY KEY, localpart TEXT NOT NULL, device_id TEXT NOT NULL, created_at_ms BIGINT NOT NULL, expires_at_ms BIGINT NOT NULL DEFAULT 0, FOREIGN KEY (localpart, device_id) REFERENCES user_devices (localpart, device_id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS user_refresh_tokens_device_idx ON user_refresh_tokens (localpart, device_id); CREATE TABLE IF NOT EXISTS user_account_data ( localpart TEXT NOT NULL REFERENCES user_accounts (localpart) ON DELETE CASCADE, -- Empty for global account data; a room ID for per-room account data. room_id TEXT NOT NULL DEFAULT '', data_type TEXT NOT NULL, content TEXT NOT NULL, PRIMARY KEY (localpart, room_id, data_type) );