e51666c52e
- 新增 web_sessions 表(sqlc):refresh_token 以 AES-256-GCM 落盘,密钥取自 CDROP_SESSION_SECRET(env、不在库内);cookie 仅存不透明随机串,表 id 存其 SHA-256,故 DB 单独泄露既换不出可用 cookie、也解不出 token - /auth/exchange 建 session 并下发 HttpOnly; Secure; SameSite=Lax; Path=/api/auth cookie,响应体不再回传 refresh_token - /auth/refresh 改为 cookie 驱动(7 天滑动失活),同时即开机静默免重登; 新增 /auth/logout(删 session + 清 cookie)、/auth/device(写设备名入 session) - device_name 随 session 存,开机 refresh 带回前端,抗 iOS PWA 存储清除; setup / 改名时 syncWebDeviceName 推送服务端 - striped per-session 锁串行化同会话并发 refresh,防一次性 refresh_token 被 花两次而把用户从所有 tab 登出 - 前端 store 移除 refreshToken 字段(长寿命凭据彻底不进 JS);main.tsx 首屏前 bootstrapAuth 用 cookie 静默续期,命中直接进已登录态、避免登录页闪现 - config:prod 强制 CDROP_SESSION_SECRET,缺失拒启动 - 桌面端不受影响(自有 loopback flow + Go keyring,不碰这两个端点)
33 lines
1.1 KiB
SQL
33 lines
1.1 KiB
SQL
-- web_sessions: browser "passwordless re-login". The opaque cookie token is
|
|
-- never stored; id holds its SHA-256, so a DB leak yields no usable cookie.
|
|
-- refresh_token is AES-256-GCM ciphertext (key lives in the container env, not
|
|
-- the DB). 7-day sliding window: every refresh rotates the token and pushes
|
|
-- expires_at forward; idle sessions are reaped by DeleteExpiredWebSessions.
|
|
|
|
-- name: CreateWebSession :exec
|
|
INSERT INTO web_sessions (id, user_id, refresh_token, device_name, user_agent, created_at, last_used_at, expires_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
|
|
|
-- name: GetWebSession :one
|
|
SELECT id, user_id, refresh_token, device_name, user_agent, created_at, last_used_at, expires_at
|
|
FROM web_sessions
|
|
WHERE id = ?;
|
|
|
|
-- name: RotateWebSession :exec
|
|
UPDATE web_sessions
|
|
SET refresh_token = ?, last_used_at = ?, expires_at = ?
|
|
WHERE id = ?;
|
|
|
|
-- name: SetWebSessionDevice :exec
|
|
UPDATE web_sessions
|
|
SET device_name = ?
|
|
WHERE id = ?;
|
|
|
|
-- name: DeleteWebSession :exec
|
|
DELETE FROM web_sessions
|
|
WHERE id = ?;
|
|
|
|
-- name: DeleteExpiredWebSessions :execrows
|
|
DELETE FROM web_sessions
|
|
WHERE expires_at < ?;
|