feat(auth): refresh token 续期(B2) — 不透明哈希+单次旋转+/auth/refresh+/auth/logout
- auth/refresh.go: RefreshStore(Issue/Rotate/Revoke/RevokeAll/RevokeByUser), SHA-256哈希256-bit随机串, 单次旋转事务(并发重放fail-closed) - 迁移0008_refresh_tokens(id/user_id/role/token_hash UNIQUE/expires_at/revoked_at + 索引) - gateway: POST /v1/auth/refresh(旋转/续期) + /v1/auth/logout(单/全设备); login响应增refreshToken - config: AUTH_REFRESH_TTL(720h)/AUTH_REFRESH_ROTATE_DISABLED; decode.go 支持time.Duration - rotate-jwt成功后RevokeAll(响应refreshTokensRevoked), revoke-keys不动refresh - 14测试+真实二进制e2e, build/vet/test绿+交叉编译 - Docs/api-client v0.25 + api-reference §2.3/2.4 同步
This commit is contained in:
1 parent
7ecb43bd5d
commit
026b27b310
17 files changed
+1237
-13
No files matched your search
@@ -0,0 +1,3 @@
|
||||
-- 0008_refresh_tokens down: drop the refresh-token table (and, implicitly, its
|
||||
-- indexes). Revoked grants carry no audit value once the feature is rolled back.
|
||||
DROP TABLE IF EXISTS refresh_tokens;
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 0008_refresh_tokens: opaque refresh tokens backing POST /api/v1/auth/refresh
|
||||
-- (B2). The long-lived credential that renews the 24h access token, so a client
|
||||
-- can stay signed in without storing the account password.
|
||||
--
|
||||
-- Only the SHA-256 hash of a token is stored (token_hash); the plaintext is
|
||||
-- returned once at login/refresh and never persisted or logged. revoked_at is
|
||||
-- NULL while a grant is live; expiry is validated in Go after the hash lookup.
|
||||
-- The canonical schema is also re-issued idempotently by auth.NewRefreshStore so
|
||||
-- an in-memory store used by tests stays self-contained; this migration is the
|
||||
-- authoritative path for an existing database.
|
||||
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TEXT NOT NULL,
|
||||
revoked_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id
|
||||
ON refresh_tokens (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_expires_at
|
||||
ON refresh_tokens (expires_at);
|
||||
Reference in new issue
Block a user