[Эпик 7] SDK CoopID-логин-фасад: handshake bind→sign→verify + lifecycle токенов + REST /coop/refresh #139

Closed
claude wants to merge 1 commits from feat/coopid-epic-7 into feat/coopid-epic-9
Owner

Фаза 3 (задача #23), stacked на feat/coopid-epic-9. Решения владельца D1+D2.

D1 — фасад в @coopenomics/auth (handshake/токены/recover/login); @coopenomics/sdk оборачивает через Client.setAccessTokenProvider (bearer в слое SDK, без импорта auth — защита Node-потребителей blago-cli/EMP).
D2/coop/session/bind отдаёт binding_token в теле (httpOnly-cookie остаётся как defense-in-depth/fallback): signTimestamp читает из него jti/sub.

Бэкенд (controller): bind→тело {binding_token,expires_in}; новый REST /coop/refresh (та же токен-машинерия, что legacy GraphQL-refresh — легаси не трогаем).
@coopenomics/auth: performTimestampHandshake (bind→signTimestamp[ключ из keystore]→verify), token-lifecycle getAccessToken+авто-refresh, recover, login=этап-1 oidc-client-ts(Authorization Code+PKCE, FR29)+handshake, logout+clearSession.
@coopenomics/sdk: setAccessTokenProvider — перед каждым GraphQL берёт свежий токен.

Жёсткий инвариант равноправия легаси-токенов закреплён token-coexistence.spec: оба контура (legacy login / auth-v2 verify) выпускают токен одним generateAuthTokens/config.jwt.secret/guard без маркера контура; refresh любого контура. Бэкенд токен-машинерию (секрет/claims/guard/legacy login+GraphQL-refresh) НЕ трогали.

Отступления (обоснования в spec-epic7-coopid-login-facade.md): ROPC запрещён (FR29) → LoginParams.password не используется (ослаблен до optional); финальная обвязка login (client_id/redirect_uri/callback) + loginWithMagicLink (recovery-confirm Эпик 3) + cutover (страница логина, e2e равноправия вживую) — фронт-фаза.

Тесты: @coopenomics/auth vitest 16/16 + tsc EXIT0 + ESLint 0; @coopenomics/sdk tsc EXIT0 + ESLint 0; controller jest 6/6 (изолированно) + ESLint 0. GraphQL-схема не менялась (/coop/* — REST) → codegen не нужен. Полный tsc controller — на CI.

🤖 Generated with Claude Code

Фаза 3 (задача #23), stacked на feat/coopid-epic-9. Решения владельца D1+D2. **D1** — фасад в `@coopenomics/auth` (handshake/токены/recover/login); `@coopenomics/sdk` оборачивает через `Client.setAccessTokenProvider` (bearer в слое SDK, без импорта auth — защита Node-потребителей blago-cli/EMP). **D2** — `/coop/session/bind` отдаёт `binding_token` в теле (httpOnly-cookie остаётся как defense-in-depth/fallback): `signTimestamp` читает из него jti/sub. **Бэкенд (controller):** bind→тело `{binding_token,expires_in}`; новый REST `/coop/refresh` (та же токен-машинерия, что legacy GraphQL-refresh — легаси не трогаем). **@coopenomics/auth:** `performTimestampHandshake` (bind→signTimestamp[ключ из keystore]→verify), token-lifecycle `getAccessToken`+авто-refresh, `recover`, `login`=этап-1 oidc-client-ts(Authorization Code+PKCE, FR29)+handshake, `logout`+clearSession. **@coopenomics/sdk:** `setAccessTokenProvider` — перед каждым GraphQL берёт свежий токен. **Жёсткий инвариант равноправия легаси-токенов** закреплён `token-coexistence.spec`: оба контура (legacy login / auth-v2 verify) выпускают токен одним `generateAuthTokens`/`config.jwt.secret`/guard без маркера контура; refresh любого контура. Бэкенд токен-машинерию (секрет/claims/guard/legacy login+GraphQL-refresh) НЕ трогали. **Отступления (обоснования в spec-epic7-coopid-login-facade.md):** ROPC запрещён (FR29) → `LoginParams.password` не используется (ослаблен до optional); финальная обвязка `login` (client_id/redirect_uri/callback) + `loginWithMagicLink` (recovery-confirm Эпик 3) + cutover (страница логина, e2e равноправия вживую) — фронт-фаза. **Тесты:** `@coopenomics/auth` vitest 16/16 + tsc EXIT0 + ESLint 0; `@coopenomics/sdk` tsc EXIT0 + ESLint 0; controller jest 6/6 (изолированно) + ESLint 0. GraphQL-схема не менялась (`/coop/*` — REST) → codegen не нужен. Полный tsc controller — на CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude added 1 commit 2026-06-14 15:59:16 +00:00
D1: фасад в @coopenomics/auth (handshake/токены/recover), @coopenomics/sdk оборачивает
через Client.setAccessTokenProvider (bearer в слое SDK, без импорта auth — защита Node-потребителей).
D2: /coop/session/bind отдаёт binding_token в теле (+ httpOnly-cookie как fallback).
Бэк: новый REST /coop/refresh (та же токен-машинерия, что и legacy GraphQL-refresh).
Инвариант равноправия токенов закреплён token-coexistence.spec (оба контура — один
generateAuthTokens/config.jwt.secret/guard, без маркера контура). Authorization Code + PKCE (FR29),
ROPC запрещён. Тесты: auth vitest 16/16, controller jest 6/6, tsc(auth+sdk) EXIT0, ESLint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant added this to the CoopID project 2026-06-15 13:17:41 +00:00
Author
Owner

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.
claude closed this pull request 2026-06-15 13:26:47 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#139