Skip to content

feat(web-api): контракт customer auth для Nuxt SSR (me / refresh / Bearer bind) - #588

Merged
biz87 merged 3 commits into
modx-pro:betafrom
Ibochkarev:feat/issue-571-customer-auth-contract
Aug 17, 2026
Merged

feat(web-api): контракт customer auth для Nuxt SSR (me / refresh / Bearer bind)#588
biz87 merged 3 commits into
modx-pro:betafrom
Ibochkarev:feat/issue-571-customer-auth-contract

Conversation

@Ibochkarev

Copy link
Copy Markdown
Member

Описание

Контракт customer auth Web API для headless/Nuxt SSR: introspection GET /customer/me, реальное продление TTL через POST /customer/token/refresh (ротация opaque token, без JWT), bind guest-корзины из валидного Bearer на login/register.

Тип изменений

  • Новая функциональность (non-breaking change)

Связанные Issues

Closes #571

Как это было протестировано?

cd core/components/minishop3
php -l src/Services/TokenService.php
php -l src/Services/Customer/CustomerSessionService.php
php -l src/Controllers/Api/Web/CustomerAuthController.php
php -l src/Middleware/TokenMiddleware.php
composer test:smoke          # exit 0, 77 tests
./vendor/bin/phpunit tests/Integration/Customer/AuthManagerLifecycleTest.php
./vendor/bin/phpunit tests/Integration/Customer/ResolveApiTokenTest.php
composer stan -- src/Services/TokenService.php src/Services/Customer/CustomerSessionService.php \
  src/Controllers/Api/Web/CustomerAuthController.php src/Middleware/TokenMiddleware.php
  • Автоматические тесты (composer test:smoke, PHPUnit lifecycle, PHPStan на затронутых файлах)
  • Ручное тестирование на живом MODX

Конфигурация тестирования:

  • MiniShop3: branch feat/issue-571-customer-auth-contractbeta
  • PHP: 8.4.17

Nuxt / SSR contract (docs для #571)

Рекомендуемый production path (BFF + Bearer)

  1. Nuxt BFF держит свою httpOnly session cookie.
  2. Server-side proxy к MODX с Authorization: Bearer <ms3_token>.
  3. Браузер не видит raw MODX token.
  4. CSRF для чистого Bearer не требуется.

Same-site cookie path

  1. GET /api/v1/customer/token/get (или auto-mint на cart).
  2. Запросы с credentials: 'include' и точным ms3_cors_allowed_origins.
  3. Cross-site cookie: Secure + SameSite=None через MODX session_cookie_* (или follow-up settings). Иначе BFF.
  4. Cookie + cross-origin без BFF — риск CSRF; предпочтителен BFF или SameSite=Lax same-site.

Запрет

Не хранить API token в localStorage / sessionStorage.

Endpoints

Method Path Auth
GET /api/v1/customer/me TokenMiddleware (guest → authenticated:false)
POST /api/v1/customer/token/refresh TokenMiddleware; ротация, старый revoke
POST /api/v1/customer/login|register без middleware; bind читает валидный Bearer → session → cookie

TTL strategy: A (refresh rotate). Sliding TTL не включён. JWT не внедрён. Отдельные ms3_token_cookie_* settings — out of scope (follow-up).

Чеклист

  • Код соответствует стилю проекта
  • Изменения не ломают существующую функциональность
  • PHPStan проходит без новых ошибок (на затронутых файлах)
  • CHANGELOG.md (релизный maintainer)

Дополнительные заметки

  • #334: ложный success-stub не возвращён; тест обновлён под реальный refresh.
  • Review fix: невалидный Bearer не маскирует guest cookie/session при bind.
  • Rotate откатывает mint, если draft sync/transfer или revoke old fails.
  • Deferred: декомпозиция TokenService (~750 LOC), DI-ключ для CustomerSessionService, Apache getallheaders beyond REDIRECT_HTTP_AUTHORIZATION.

@Ibochkarev Ibochkarev added priority: medium Средний приоритет enhancement New feature or request labels Aug 16, 2026
@Ibochkarev
Ibochkarev requested a review from biz87 August 16, 2026 12:18
@biz87

biz87 commented Aug 17, 2026

Copy link
Copy Markdown
Member

Привет! Влил по твоему порядку #584 и #583 — оба в beta, issues #576 и #572 закрыты.

Этот PR нужно ребейзнуть на текущую beta: конфликт в TokenMiddleware.php семантический, руками разруливать не стал, чтобы не потерять security-фикс.

Суть конфликта. PR писался до вливания #584, и в нём удалены/перенесены методы, которые #584 как раз добавил:

Плюс собственный тест PR после мержа с beta падает:

FAIL: TokenMiddleware must not keep a private resolveToken duplicate: expected false
(tests/CustomerSessionContractTest.php)

То есть контракт «резолв живёт в одном месте» конфликтует с тем, как метод выглядит после #584. При ребейзе важно, чтобы strip query-токенов сохранился в том слое, куда переедет резолв, — иначе #576 частично откатится.

Мелочь заодно: файл tests/TokenRefreshRouteRemovedTest.php теперь проверяет обратное тому, что написано в имени — что роут POST /customer/token/refresh зарегистрирован. Стоит переименовать (например TokenRefreshRouteContractTest), иначе через полгода это будет сбивать с толку.

Сам код посмотрел, вопросов нет: refreshToken() реально ротирует токен через rotateApiToken() (не заглушка, как было в #351), /me отдаёт CustomerPublicDto и из токена только expires_at/customer_id. Как ребейзнёшь — сразу провалидирую и вольём.

Дальше по твоему списку иду: #586, #587, #595, потом #580 после ребейза, #585 отдельно.

@Ibochkarev
Ibochkarev force-pushed the feat/issue-571-customer-auth-contract branch 2 times, most recently from 14052e2 to 1b8c87c Compare August 17, 2026 09:06
…arer bind)

Add GET /customer/me introspection, real POST /customer/token/refresh rotation,
and validate Bearer before binding guest cart on login/register.
TokenMiddleware no longer owns resolveToken(); modx-pro#576 checks target
TokenService::resolveTokenFromRequest after the modx-pro#571 auth rebase.
@Ibochkarev
Ibochkarev force-pushed the feat/issue-571-customer-auth-contract branch from bda8377 to baa55f4 Compare August 17, 2026 09:19
@biz87
biz87 merged commit 47c9cb3 into modx-pro:beta Aug 17, 2026
3 checks passed
Ibochkarev added a commit that referenced this pull request Aug 17, 2026
After rebase onto beta with customer auth contract, middleware calls
syncSessionFromToken(); journey doubles must implement it.
biz87 pushed a commit that referenced this pull request Aug 18, 2026
* test(web-api): add headless Nuxt journey suite via Router envelope

Catch storefront wire regressions (middleware, HTTP status, cart→login
transfer) that domain-only tests miss. Closes the Phase A+B gate for #574.

* fix(test): load Sqlite draft harness via bootstrap on Linux

PSR-4 maps Support/ but files live in tests/support/; CI is
case-sensitive and failed with trait not found (#595).

* test(web-api): align journey suite with beta CORS and mint contracts

After rebase onto #584/#583: use CorsConfig::isOriginAllowed for preflight
smoke, and expect 500/internal mint failure instead of 401.

* test(web-api): sync Journey token stubs with TokenMiddleware (#588)

After rebase onto beta with customer auth contract, middleware calls
syncSessionFromToken(); journey doubles must implement it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: medium Средний приоритет

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Web API: контракт customer auth для Nuxt SSR (me / TTL / cookie+Bearer)

2 participants