0.7.0
Added
- Миграции
018_oauth_sessionsи019_refresh_token_revocation(SQLite + PG) - Таблица
oauth_sessions(id, user_id, expires_at, created_at)для серверного хранения коротких OAuth-сессий - Колонка
refresh_tokens.revoked_at(soft-revoke), индексidx_refresh_tokens_family - Тип
store.OAuthSessionи методыCreateOAuthSession/GetOAuthSession/DeleteOAuthSession/DeleteExpiredOAuthSessions - Тесты криптографии аутентификации:
internal/auth/tokens_test.go(HS256-only, alg=none/HS512 rejection, tampered payload),internal/auth/oauth_test.go(refresh happy-path, reuse-detection с family revoke, family isolation),internal/server/handler/auth_test.go(forged cookie, random session ID, legitimate session, cookie flags)
Changed
- JWT-подпись переведена на
crypto/hmac.New(sha256.New, secret)вместоsha256(data || secret).ValidateAccessTokenпарсит header и принимает толькоalg=HS256. Сравнение подписи черезhmac.Equal(constant-time) - Cookie
tc_sessionтеперь хранит только opaque session ID (32 байта random); содержимое (user_id,expires_at) — в БД. Cookie выставляется сHttpOnly,Secure(в prod),SameSite=Strict,MaxAge=5min - Refresh family-rotation:
generateFamilyID()создаёт случайный 32-байтный family ID при первичном login; при refresh новый токен наследуетfamily; повторное использование revoked токена → revoke всей семьи (reuse-detection) RevokeRefreshToken/RevokeRefreshTokenFamilyтеперь делают UPDATErevoked_at, а не DELETE — запись сохраняется для обнаружения reuse-attack- Singleton refresh promise в
web/src/lib/api/client.ts: одновременные 401 делят один refresh-вызов, чтобы избежать ложного reuse-detect и потери семьи request/rawRequestв client.ts отрефакторены в общие helpersbuildHeaders/buildInit/parseError; парсинг ошибок проверяет Content-Type и не падает на не-JSON ответах
Fixed
- C-1: JWT length-extension и отсутствие проверки
alg(любой токен сalg=noneилиalg=HS512мог проходить как валидный) - C-2: подделка cookie
tc_session = {"user_id":N}для входа за любого пользователя - H-4:
Family = hashToken(refreshToken)ломал reuse-detection (отзывался только сам токен) - H-5: гонка параллельных
tryRefreshна фронте, ложно вызывавшая reuse-detect и revoke всей семьи