-- Identity, login states and the login journal. -- name: PutLoginState :exec insert into auth_states (state_sha256, provider, issuer, nonce, code_verifier, return_to, start_id, created_at, expires_at) values (sqlc.arg(state_sha256), sqlc.arg(provider), sqlc.arg(issuer), sqlc.arg(nonce), sqlc.arg(code_verifier), sqlc.arg(return_to), sqlc.arg(start_id), sqlc.arg(created_at), sqlc.arg(expires_at)); -- name: TakeLoginState :one -- Deleting and returning in ONE statement is what makes it single-use under concurrency: a second -- callback with the same state deletes nothing and gets nothing, with no window between the check -- and the removal. delete from auth_states where state_sha256 = sqlc.arg(state_sha256) and expires_at > sqlc.arg(now) returning provider, issuer, nonce, code_verifier, return_to, start_id, created_at, expires_at; -- name: DeleteExpiredLoginStates :execrows delete from auth_states where expires_at <= sqlc.arg(now); -- name: DeleteOldLoginEvents :execrows -- /auth/callback writes a row on every refusal and needs no credential to do it, so a journal that -- only grows is a liability rather than an audit. delete from login_events where at < sqlc.arg(before); -- name: UserByIdentity :one -- Resolves a provider's subject to the account it belongs to, WITHOUT creating one. select user_id from identities where provider = sqlc.arg(provider) and subject = sqlc.arg(subject); -- name: LockIdentity :one select user_id from identities where provider = sqlc.arg(provider) and subject = sqlc.arg(subject) for update; -- name: RefreshIdentity :exec -- The address is refreshed only when the provider says it is verified — an unverified one is kept -- on the identity and never promoted to the account. update identities set email = sqlc.narg(email), email_verified = sqlc.arg(email_verified), last_login_at = sqlc.arg(now) where provider = sqlc.arg(provider) and subject = sqlc.arg(subject); -- name: RefreshAccountEmail :exec -- The cast keeps the parameter non-null: users.email IS nullable, but this path is reached only for -- a VERIFIED, non-empty address, and a *string here would invite a nil the branch cannot produce. update users set email = sqlc.arg(email)::text where id = sqlc.arg(id); -- name: CreateUser :exec insert into users (id, email, created_at) values (sqlc.arg(id), sqlc.narg(email), sqlc.arg(created_at)); -- name: CreateIdentity :execrows -- `do nothing` rather than a bare insert: two first logins of one brand-new identity can race, and -- the loser must see the row the winner inserted rather than a unique violation. insert into identities (provider, subject, user_id, email, email_verified, created_at, last_login_at) values (sqlc.arg(provider), sqlc.arg(subject), sqlc.arg(user_id), sqlc.narg(email), sqlc.arg(email_verified), sqlc.arg(now), sqlc.arg(now)) on conflict (provider, subject) do nothing; -- name: RevokeUserSessions :execrows update sessions set revoked_at = sqlc.arg(now)::timestamptz where user_id = sqlc.arg(user_id) and revoked_at is null; -- name: RecordLogin :exec insert into login_events (user_id, provider, outcome, reason, ip_prefix, client, at) values (sqlc.narg(user_id), sqlc.arg(provider), sqlc.arg(outcome), sqlc.arg(reason), sqlc.arg(ip_prefix), sqlc.arg(client), sqlc.arg(at)); -- name: RecentLogins :many select provider, outcome, reason, ip_prefix, client, at from login_events where user_id = sqlc.arg(user_id)::text order by at desc limit sqlc.arg(lim)::bigint;