-- Signing in from a terminal, for clients that cannot open a browser on the -- machine they run on (the TUI over SSH is the reason). -- -- The flow is the OAuth device authorization grant, run by terdut itself rather -- than the identity provider, so the terminal never talks to the provider and -- the server issues its ordinary session at the end: -- -- 1. The terminal asks for a login and gets two secrets: a device code it -- keeps and polls with, and a short user code it shows the person. -- 2. The person opens the verification URL on any device, signs in by whatever -- means the server offers, sees the user code, and approves it. -- 3. The terminal's next poll finds the row approved and is given a session. -- -- Only the hash of the device code is stored, like every other token here: the -- device code is what earns a session, so a database read must not yield one. -- The user code is shown on screens and typed by people, so it is stored as is; -- on its own it can only be approved, never redeemed. -- -- user_id is the person who approved. It is empty until then, and the session -- is minted at redemption, not at approval: an approval nobody collects must not -- leave a live session lying about. -- -- last_polled_at lets the server refuse a client that polls faster than the -- interval it was told. CREATE TABLE device_logins ( device_hash TEXT PRIMARY KEY, user_code TEXT NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'denied')), user_id BIGINT REFERENCES users(id) ON DELETE CASCADE, expires_at BIGINT NOT NULL, last_polled_at BIGINT NOT NULL DEFAULT 0 ); CREATE INDEX device_logins_expires_idx ON device_logins (expires_at); -- Where to send the browser once a single sign-on login completes. A person who -- opens /device?code=... without a session has to sign in first and then come -- back to it, and the same is true of any other deep link. Validated when it is -- stored: only a path on this server is ever kept. ALTER TABLE oidc_logins ADD COLUMN next TEXT NOT NULL DEFAULT '/';