feat(auth): refresh token 续期(B2) — 不透明哈希+单次旋转+/auth/refresh+/auth/logout
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 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:
AskaEth committed 2026-09-08 21:45:00 +08:00
1 parent 7ecb43bd5d
commit 026b27b310
17 files changed
+1237 -13

No files matched your search

+33 -1
View File
@@ -6,6 +6,7 @@ import (
"reflect"
"strings"
"testing"
"time"
)
func writeEnvFile(t *testing.T, content string) string {
@@ -130,6 +131,38 @@ func TestLoad_MissingDotEnvIsTolerated(t *testing.T) {
}
}
func TestLoad_AuthRefreshDefaults(t *testing.T) {
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
cfg, err := Load("")
if err != nil {
t.Fatalf("Load() error: %v", err)
}
if cfg.Auth.RefreshTTL != 720*time.Hour {
t.Errorf("Auth.RefreshTTL = %v, want 720h", cfg.Auth.RefreshTTL)
}
if cfg.Auth.RefreshRotateDisabled {
t.Errorf("Auth.RefreshRotateDisabled = true, want false (rotation on by default)")
}
}
func TestLoad_AuthRefreshEnvOverrides(t *testing.T) {
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
t.Setenv("AUTH_REFRESH_TTL", "30m")
t.Setenv("AUTH_REFRESH_ROTATE_DISABLED", "true")
cfg, err := Load("")
if err != nil {
t.Fatalf("Load() error: %v", err)
}
if cfg.Auth.RefreshTTL != 30*time.Minute {
t.Errorf("Auth.RefreshTTL = %v, want 30m", cfg.Auth.RefreshTTL)
}
if !cfg.Auth.RefreshRotateDisabled {
t.Errorf("Auth.RefreshRotateDisabled = false, want true")
}
}
func TestLoad_CapacityDefaults(t *testing.T) {
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
@@ -144,7 +177,6 @@ func TestLoad_CapacityDefaults(t *testing.T) {
t.Errorf("Capacity.MinConfidence = %v, want 0.6", cfg.Capacity.MinConfidence)
}
}
func TestLoad_CapacityEnvOverrides(t *testing.T) {
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
t.Setenv("CAPACITY_ENABLED", "true")