-- §6.2 / v0.11.0: trust device for 30 days (roadmap item #9).
--
-- After a successful OTC or passcode sign-in, the user can check
-- "trust this device for 30 days." The framework then issues a
-- server-issued opaque device-trust token, stores its hash on this
-- table, and sets a long-lived HttpOnly + Secure + SameSite=Lax
-- cookie carrying the raw token. On a subsequent visit, the cookie is
-- presented at `/auth/device-trust/start`; if the server can match the
-- hash to a non-expired non-revoked row, the user is signed in without
-- another OTC / passcode round-trip.
--
-- v0.11.0 introduces no new env vars. The 30-day window is hard-coded
-- in `backend/app/device_trust.py`; raising or lowering it (or making
-- it user-selectable) is a §19.2 candidate, alongside the cross-device
-- session-revocation surface this table will eventually share with the
-- v0.10.0 passcode-lockout shape (see SPEC §19.2 / SESSIONS-AND-DEVICES).
--
-- Storage shape:
--
--   * `id` — surrogate key. Lets the revoke-device UI address a single
--     row by id without leaking the token shape.
--   * `user_id` — FK into users(id) with cascade on delete. A deleted
--     user automatically loses every trusted device.
--   * `device_token_hash` — bcrypt hash of the random opaque token
--     issued at trust-time. The raw token only ever lives in the
--     outbound `Set-Cookie` header and the inbound `Cookie` header;
--     server-side storage is the hash, so a DB compromise does not
--     hand attackers a stash of valid device tokens.
--   * `created_at` — when the row was issued.
--   * `expires_at` — `created_at + 30 days`. A row past this timestamp
--     is dead; the lookup path refuses it without further checks.
--   * `user_agent` — the User-Agent header captured at issuance.
--     Stored verbatim (truncated to 1024 chars at the application
--     layer) so the revoke-device UI can show a rough device label.
--     Not used for any auth decision — purely a hint to the user
--     reviewing their device list.
--   * `last_seen_at` — refreshed every time the row authenticates a
--     request. Lets the revoke-device UI surface "last used 3 days
--     ago" so the user can tell which row corresponds to which
--     device.
--   * `revoked_at` — NULL means active; non-NULL stamps when the user
--     (or admin) revoked the row. Lookups treat any non-NULL value
--     as "this row is dead" without consulting the expiry; the
--     revoke gesture is intentionally one-way (a revoked device must
--     re-trust to come back online).
--
-- Indexing: a unique index on `device_token_hash` so collisions are
-- detectable at insert time (the token space is 256 bits of CSPRNG
-- entropy, so a collision is structurally impossible, but the
-- declaration documents the invariant). A separate index on
-- `(user_id, revoked_at)` so the revoke-device UI's list query is
-- a covering walk.
--
-- The bcrypt dependency reused here was added in v0.7.0 for OTC and
-- extended in v0.10.0 for passcodes; v0.11.0 needs no new dep.
--
-- The cookie shape: `rfc_device_trust` carries the raw token,
-- HttpOnly, Secure, SameSite=Lax, Max-Age=2592000 (30 days). It is
-- "essential" per the v0.13.0 cookie-consent banner (it is part of
-- authentication, not analytics), so it is set regardless of the
-- user's analytics / other-cookies choices.

CREATE TABLE device_trust (
    id                  INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id             INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    device_token_hash   TEXT NOT NULL,
    created_at          TEXT NOT NULL DEFAULT (datetime('now')),
    expires_at          TEXT NOT NULL,
    user_agent          TEXT NOT NULL DEFAULT '',
    last_seen_at        TEXT NOT NULL DEFAULT (datetime('now')),
    revoked_at          TEXT
);

CREATE UNIQUE INDEX idx_device_trust_token_hash ON device_trust (device_token_hash);
CREATE INDEX idx_device_trust_user ON device_trust (user_id, revoked_at);
