feat(auth): шаг 0.6 — TTL access 15 минут + хэширование refresh/reset-токенов в БД
Some checks are pending
Branch Check / Type Check & Build (push) Waiting to run

- server/utils/jwt.ts: дефолт JWT_ACCESS_EXPIRES 30d → 15m (env сохранено), hashToken() (sha256 hex)
- migrations/0082_token_hashes.sql: *_hash колонки в user_sessions/users, DELETE FROM user_sessions (глобальный разлогин), legacy plain-колонки сохранены, но не пишутся
- storage: lookup/отзыв сессий и reset-токенов по хэшу; markSessionReplaced по id сессии
- auth.service: ротация в rotateFamilySession(), grace-period ротирует активную сессию вместо возврата plain-токена
- auth.core.routes: forgot/reset-password пишут/ищут sha256-хэш, plain только в письме
- tests/token-security.test.ts: 12 тестов (выпуск по хэшу, ротация, reuse detection, grace, reset)
- .env.example, swagger, IMPLEMENTATION_LOG.md обновлены

Проверки: npm run check чисто, vitest 118/118, lint 0 errors, build собирается
This commit is contained in:
2026-09-08 11:35:32 +03:00
parent 639036c87e
commit 06bfd2aa75
11 changed files with 506 additions and 60 deletions

View File

@@ -65,11 +65,12 @@ export interface IStorage {
deleteUserOfflineSubscription(userId: number, formId: number): Promise<void>;
// User sessions
// Токены передаются в plain-виде; хэширование (sha256) — внутри реализации (шаг 0.6).
createUserSession(session: InsertUserSession): Promise<UserSession>;
getUserSessionByToken(refreshToken: string): Promise<UserSession | undefined>;
getUserSessionByParentToken(parentRefreshToken: string): Promise<UserSession | undefined>;
getActiveSessionInFamily(familyId: string): Promise<UserSession | undefined>;
markSessionReplaced(refreshToken: string, replacedByToken: string): Promise<void>;
markSessionReplaced(sessionId: number, replacedByToken: string): Promise<void>;
revokeSession(refreshToken: string): Promise<void>;
revokeSessionFamily(familyId: string): Promise<void>;
revokeAllUserSessions(userId: number): Promise<void>;