Skip to content

feat(account): refresh-токены с ротацией и отзывом (итер. 36) - #41

Merged
Azazlokus merged 1 commit into
mainfrom
feat/auth-refresh
Aug 18, 2026
Merged

Azazlokus merged 1 commit into
mainfrom
feat/auth-refresh

Conversation

@Azazlokus

Copy link
Copy Markdown
Owner

Что

Итерация 36 — refresh-токены + ротация + хардненинг auth. Один самодостаточный
HMAC-токен (TTL 24 ч) для API и game-join разбит на короткий access (формат и
Verify не тронуты — join прежний) + долгий refresh с ротацией, серверным
хранением и детекцией переиспользования.

Зачем

Раньше утёкший токен был валиден сутки, а отозвать сессию / разлогинить на сервере
было нельзя. Теперь: access живёт максимум accessTTL (деф. 15m), refresh отзывается
(logout), а повторное предъявление отозванного refresh гасит всё семейство (детект кражи).

Как

  • store — миграция 0002 (sqlite+postgres) refresh_tokens: в БД только SHA-256
    токена, family_id — цепочка ротаций одного логина. Методы CreateRefreshToken,
    RefreshTokenByHash, RotateRefreshToken (транзакция: атомарный отзыв старого +
    вставка нового; при уже отозванном → ErrTokenRevoked без вставки), RevokeRefreshFamily;
    просроченные токены аккаунта чистятся в той же ротации (таблица ограничена сроком жизни).
  • account — NewService(store, secret, accessTTL 15m, refreshTTL 720h);
    Register/Login/Guest → Tokens{Access, Refresh, ExpiresIn} (гость эфемерен — без
    refresh); Refresh (ротация; отозванный → гасим семейство) и Logout. Access остаётся
    HMAC (constant-time hmac.Equal), refresh — crypto/rand 32B, хранится как SHA-256.
  • api — POST /api/refresh (под рейт-лимитом — минтит токены) + POST /api/logout
    (204, идемпотентно); в ответы register/login добавлены refresh_token + expires_in.
    Единый ErrBadToken→401 для невалидного/просроченного/переиспользованного (без энумерации).
  • cmd/server — env ARENA_ACCESS_TTL/ARENA_REFRESH_TTL вместо ARENA_TOKEN_TTL.
  • web — хранит оба токена в localStorage, обновляет access перед join и по 401
    (ensureFreshAccess/refreshAccess), logout отзывает refresh на сервере.
  • docs — README RU/EN + docs/architecture.md.

Границы

Провод (internal/protocol) и симуляция не тронуты (access-токен и join те же) →
игровые стражи (concurrency/determinism/protocol) не гонялись (прецедент итер. 21).
Это бэкенд-фича безопасности → прогнан security-review: HIGH/MEDIUM-findings нет.

Тестирование

  • make check (vet + -race все пакеты) — зелёный.
  • make integration — зелёный.
  • Новое покрытие: store (жизненный цикл refresh, ротация, ErrTokenRevoked, отзыв
    семейства, уборка просроченных — на SQLite; Postgres-сьют дополнен refresh_tokens),
    account (ротация, детект переиспользования гасит семейство, logout, невалидный/
    просроченный refresh, гость без refresh), api (эндпоинты refresh/logout, реюз→401,
    logout→204), cmd/server (TestAuthTTLConfig).
  • Live-smoke бинаря: register→refresh(ротация)→реюз старого(401)→новый тоже мёртв(401,
    семейство погашено)→logout(204); гость без refresh_token.
  • node --check web/game.js.

BENCHMARKS.md не трогался — горячего пути нет.

…итер. 36)

Короткоживущий access-токен (join + API, формат и Verify не тронуты) обновляется
долгоживущим refresh-токеном с ротацией, серверным хранением и детекцией
переиспользования — теперь сессию можно отозвать (logout), а утёкший access живёт
максимум accessTTL.

- store: миграция 0002 (sqlite+postgres) refresh_tokens (в БД только SHA-256,
  family_id — цепочка ротаций); методы CreateRefreshToken/RefreshTokenByHash/
  RotateRefreshToken (tx: атомарный отзыв старого + вставка нового, при уже отозванном
  ErrTokenRevoked)/RevokeRefreshFamily; уборка просроченных в ротации.
- account: NewService(store, secret, accessTTL 15m, refreshTTL 720h); Register/Login/
  Guest → Tokens{Access,Refresh,ExpiresIn} (гость без refresh); Refresh (ротация;
  повторное предъявление отозванного гасит семейство) и Logout.
- api: POST /api/refresh (под рейт-лимитом) + POST /api/logout (204); в ответы
  register/login добавлены refresh_token + expires_in.
- cmd/server: env ARENA_ACCESS_TTL/ARENA_REFRESH_TTL вместо ARENA_TOKEN_TTL.
- web: хранит оба токена, обновляет access перед join и по 401, logout отзывает refresh.
- docs: README RU/EN + architecture.

Провод/симуляция не тронуты (игровые стражи не гонялись); security-review — findings
нет. make check + integration зелёные.
@Azazlokus
Azazlokus merged commit 02ad371 into main Aug 18, 2026
10 checks passed
@Azazlokus
Azazlokus deleted the feat/auth-refresh branch August 18, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant