# Authentication

Vesremont: бесплатный публичный каталог и read-only sandbox. Первый GET выполняется без человека; необязательный anonymous credential не даёт покупательских прав. Стоимость API — 0 RUB; [возможности и квоты](https://vesremont.com/pricing.md).

## Start here: первый API-вызов без человека

[GET /api/v1/products?per_page=1](https://vesremont.com/api/v1/products?per_page=1) → `items` и числовой `id`. Не нужны API key, аккаунт, cookies, CSRF или OAuth-согласие. Не передавайте Authorization для zero-auth. [GET /oauth/sandbox](https://vesremont.com/oauth/sandbox) → синтетический JSON, `read_only: true`, `orders_supported: false`.

## Step 1 — Discover

Прочитайте [PRM](https://vesremont.com/.well-known/oauth-protected-resource/api/v1) → `authorization_servers` → [AS metadata](https://vesremont.com/.well-known/oauth-authorization-server). В `agent_auth` используйте `skill`, `identity_endpoint`, `identity_types_supported` и `sandbox_endpoint`. Приватный API возвращает 401 и `WWW-Authenticate: Bearer resource_metadata="https://vesremont.com/.well-known/oauth-protected-resource/api/v1"`.

## Step 2 — Pick a method

Каталог: zero-auth или `anonymous` с `catalog:read`. Внешние `identity_assertion` / `id-jag` и `service_auth` не поддерживаются. Личные корзина, checkout, заказы: отдельное CIMD/PKCE S256 согласие покупателя, не anonymous credential.

## Step 3 — Register

[GET /oauth/identity](https://vesremont.com/oauth/identity) публикует шаблон. Отправьте `POST https://vesremont.com/oauth/identity`, `Content-Type: application/json`, тело `{"type":"anonymous"}`. Сохраните в памяти `registration_id` и выданный `identity_assertion`; assertion действует 300 секунд. Не передавайте email, пароль или чужой assertion.

## Step 4 — Claim

Пропустите: `claim_supported: false`; claim endpoint не опубликован. Anonymous нельзя превратить в покупательское разрешение. Не имитируйте Claim и не повышайте scopes.

## Step 5 — Exchange the assertion

Один form-urlencoded `POST https://vesremont.com/oauth/token`: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`, `assertion=<полученный identity_assertion>`, `scope=catalog:read`, `resource=https://vesremont.com/api/v1`. Кодируйте значения через URLSearchParams. Ответ: `access_token`, `token_type`, `expires_in` (900 секунд), `scope`. Refresh token не выдаётся.

## Step 6 — Use the access_token

Выполните `GET https://vesremont.com/api/v1/products?per_page=1` с `Authorization: Bearer <access_token>`. Результат — `items` и ID товаров. Токен действует только для REST-каталога, не MCP/A2A/UCP или данных покупателя. Не сохраняйте token/assertion в URL, файлах или логах.

## Step 7 — Errors

400 при повторном/просроченном assertion или неверном resource/scope — не повторяйте его; прочитайте OAuth error. 401 при истекшем/отозванном token — удалите его. При 429 дождитесь Retry-After. 403 не даёт права обходить scope/ownership. Для личной операции получите отдельное согласие; неопределённую отправку заказа не повторяйте с новым Idempotency-Key.

## Step 8 — Revocation

Form-urlencoded `POST https://vesremont.com/oauth/revoke`: `token=<access_token>` и `client_id=<registration_id>`. Затем удалите credential. Предъявление отозванного token получает 401; независимое публичное чтение без credentials остаётся доступным. Истечение требует новой anonymous регистрации, не человека.

## Подробности: покупатель, browser WebMCP и одно общее согласие

Business name: Vesremont

Brand: Vesremont

Legal entity: ООО «Афганец»

Description: Vesremont — русскоязычный интернет-магазин товаров для ремонта, строительства, дома и сада: инструмента, электрики, стройматериалов, крепежа, сантехники, оборудования, спецодежды и товаров для профессиональных задач. Публичный каталог содержит актуальные страницы категорий, брендов и товаров; цены, остатки и возможность заказа проверяются на текущей странице и в корзине.

Services: инструменты, электрика, строительные материалы, крепёж, сантехника, оборудование, спецодежда, товары для дома и сада

### Публичный доступ и границы покупательского разрешения

Для публичного каталога не нужны аккаунт, API key, cookies, CSRF, регистрация или браузерное согласие. Выполните [GET https://vesremont.com/api/v1/products?per_page=1](https://vesremont.com/api/v1/products?per_page=1), прочитайте `items` и числовой `id` товара. Это законченный zero-auth onboarding, а не переход на страницу авторизации. Для этого вызова не передавайте Authorization; неверный или отозванный Bearer token будет отклонён.

[GET /oauth/sandbox](https://vesremont.com/oauth/sandbox) — отдельный синтетический read-only пример без заказов. [GET /oauth/identity](https://vesremont.com/oauth/identity) публикует шаблон самостоятельной регистрации `{"type":"anonymous"}` для необязательного catalog credential. Register → Exchange → Use → Revocation полностью автоматические; точные поля описаны ниже в «Автоматический onboarding без покупателя». [Стоимость, возможности и квоты](https://vesremont.com/pricing.md).

Сессионные ограничения разделов Browser WebMCP ниже не относятся к этому публичному GET. Для личных корзины, избранного, checkout, заказов и webhooks требуется согласие покупателя; anonymous credential его не заменяет. Заказ по-прежнему подтверждается отдельно.

## Interaction mode

Interaction mode: `browser_webmcp`

Runtime: `document.modelContext`

Authentication: `browser_session_and_csrf`

Browser WebMCP работает внутри открытой страницы сайта с её browser session и CSRF. Удалённые REST, MCP, A2A и UCP работают по собственному контракту OAuth PKCE, описанному ниже. Не переносите browser cookies во внешний клиент и не используйте access token как CSRF-токен.

## Browser session

WebMCP-инструменты работают в контексте текущей браузерной сессии Vesremont. Корзина, избранное и состояние checkout относятся к этой сессии, поэтому после навигации или изменения данных агент повторно читает `document.modelContext` и актуальное состояние страницы.

## CSRF protection

Изменяющие состояние WebMCP-действия используют серверную CSRF-проверку текущей сессии. Агент вызывает опубликованные инструменты через `document.modelContext` и не обращается напрямую к внутренним Bitrix/AJAX endpoint.

## Guest checkout

Предварительная регистрация или вход перед checkout не требуются: `login_required_before_checkout = false`. Покупатель указывает контактные данные при оформлении; сервер создаёт или связывает внутреннюю учётную запись в рамках обработки заказа.

## Calling WebMCP tools

1. Открыть Vesremont в браузере с поддержкой WebMCP.
2. Прочитать текущий `document.modelContext` и доступный для страницы список инструментов.
3. Вызвать нужный опубликованный инструмент в контексте текущей browser session.
4. После перехода на другую страницу снова прочитать `document.modelContext`, потому что набор контекстных инструментов может измениться.

## Creating an order

1. Вызвать `prepare_order` и получить полную актуальную сводку и одноразовый token подтверждения заказа.
2. Показать сводку пользователю и получить явное подтверждение.
3. Один раз вызвать `submit_order` с `confirmed: true` и полученным token.
4. При изменении корзины, checkout или истечении token повторить подготовку и подтверждение.

Token из `prepare_order` подтверждает только конкретную подготовленную сводку заказа. Это не login token, access token или OAuth credential.

## Remote access

Удалённый доступ отделён от browser WebMCP. Публичное чтение каталога не требует токена. Корзина, избранное, checkout и заказы требуют разрешения покупателя и Bearer access token; cookies удалённого клиента не выбирают корзину покупателя.

## Автоматический onboarding без покупателя

Начать можно сразу с `GET https://vesremont.com/api/v1/products?per_page=1`: ни человек, ни API key не нужны. Для изолированного знакомства с форматом есть [read-only sandbox](https://vesremont.com/oauth/sandbox): синтетические данные, без Bitrix-корзины, покупателей, оплаты и создания заказов. Это не полный тестовый магазин.

Если клиенту нужен собственный короткоживущий credential, в AS metadata прочитайте `agent_auth.identity_endpoint` и `identity_types_supported`. Поддерживается только `anonymous`; внешние `identity_assertion` (ID-JAG), `service_auth` и перенос права покупателя через Claim не поддерживаются. Не отправляйте email, пароль или чужое identity assertion.

1. Discover: PRM `/.well-known/oauth-protected-resource` → его `authorization_servers` → AS metadata → `agent_auth.skill` и `identity_endpoint`.
2. Register: `POST https://vesremont.com/oauth/identity`, `Content-Type: application/json`, тело `{"type":"anonymous"}`. Публичный GET этого URL возвращает реальный шаблон запроса. Ответ POST: `registration_id`, подписанный `identity_assertion`, `assertion_expires`, `pre_claim_scopes: ["catalog:read"]`.
3. Claim: пропустить. `claim_supported: false`; эта регистрация никогда не превращается в покупательское разрешение.
4. Exchange: form-urlencoded POST `/oauth/token` с `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`, `assertion=<полученный identity_assertion>`, `scope=catalog:read`, `resource=https://vesremont.com/api/v1`. Assertion одноразовый, живёт 5 минут; access token — 15 минут. Это не обмен внешнего ID-JAG и не refresh.
5. Use: `GET /api/v1/products?per_page=1` с `Authorization: Bearer <access_token>`. Только публичное чтение REST-каталога; токен не подходит для MCP/A2A/UCP, корзины, checkout, заказов или webhooks. Без токена публичный каталог тоже доступен.
6. Revocation: POST `/oauth/revoke` с `token` и `client_id=<registration_id>`. Предъявление отозванного anonymous token получает 401; независимый публичный GET без credentials остаётся доступен.

Не сохраняйте assertion/token в URL, исходниках или логах. Повтор assertion и неправильный resource возвращают OAuth error; повышение scope запрещено. Истечение требует новой анонимной регистрации, не человека. Покупательские действия проходят отдельный CIMD/PKCE путь ниже с явным согласием и подтверждением заказа.

## Короткий путь: одно согласие для нескольких интерфейсов

Если агенту нужны REST, MCP, A2A и UCP, покупатель может разрешить их на **одной странице**. Это дополнительный путь; прежние восемь этапов и отдельное согласие для одного интерфейса сохраняются. Общее разрешение не является подтверждением заказа.

1. Создайте обычный Authorization Code + PKCE S256 запрос. В `resource` укажите основной интерфейс, например `https://vesremont.com/api/v1`. В дополнительном параметре Vesremont `consent_resources` перечислите нужные resource URL через пробел. Список должен содержать основной resource, без повторов и посторонних адресов. Используйте `URLSearchParams`, не склеивайте URL вручную.
2. В `scope` запросите только права, нужные задаче. Покупатель видит все интерфейсы и права и один раз нажимает «Разрешить доступ» либо «Отказать». Разрешённые scopes действуют для каждого явно перечисленного интерфейса; новых прав при обмене не появляется. UCP имеет собственные manage scopes — не заменяйте ими REST scopes и не запрашивайте их только ради публичного каталога.
3. Обменяйте code **один раз** для основного resource, как описано в Exchange ниже. Для остальных одобренных интерфейсов получите отдельные Bearer tokens через тот же `/oauth/token`, используя ограниченный [OAuth Token Exchange (RFC 8693)](https://www.rfc-editor.org/rfc/rfc8693.html).

Пример параметров общего согласия для чтения корзины через REST/MCP/A2A:

```javascript
const params = new URLSearchParams({
  response_type: 'code', client_id, redirect_uri, state,
  code_challenge, code_challenge_method: 'S256',
  resource: 'https://vesremont.com/api/v1', scope: 'cart:read',
  consent_resources: [
    'https://vesremont.com/api/v1',
    'https://vesremont.com/mcp',
    'https://vesremont.com/a2a'
  ].join(' ')
});
const authorizationUrl = 'https://vesremont.com/oauth/authorize?' + params;
```

Для UCP добавьте `https://vesremont.com/ucp/v1` в тот же список и только необходимые UCP scopes. После первичного обмена отправьте form-urlencoded POST:

```text
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token_type=urn:ietf:params:oauth:token-type:access_token
subject_token=<исходный access_token основного интерфейса>
client_id=<тот же client_id>
resource=https://vesremont.com/mcp
scope=cart:read
```

`scope` можно только сузить; без него сохраняются scopes исходного токена. Ответ содержит `access_token`, `issued_token_type`, `token_type`, `expires_in`, `scope`. Каждый выданный токен имеет **одну аудиторию**: REST token по-прежнему не принимается MCP/A2A/UCP. Полученные таким обменом токены нельзя обменивать дальше. Прежнее разрешение без списка дополнительных интерфейсов не даёт доступа к ним.

Обмен не продлевает срок: новый токен истекает не позже исходного токена и разрешения. Refresh tokens не выдаются. Отзыв исходного токена прекращает использование всех его производных; отзыв одного производного прекращает только его доступ. Отзыв общего разрешения покупателем закрывает все связанные интерфейсы. Для продолжения после истечения снова требуется согласие покупателя.

Дальнейшая подготовка выполняется агентом автоматически в пределах разрешения. **Конкретный заказ** покупатель отдельно подтверждает на сайте по реальной сводке товаров, суммы, получения, оплаты и контактов. CSRF/Origin, живая привязка к покупателю, ownership и Idempotency-Key остаются обязательными.

## Discover

Шаг 1 — обнаружить авторизацию.

Прочитайте [OAuth metadata](https://vesremont.com/.well-known/oauth-authorization-server) и [OpenAPI](https://vesremont.com/openapi.json). Их Markdown-представления: [OAuth walkthrough](https://vesremont.com/.well-known/oauth-authorization-server.md) и [API guide](https://vesremont.com/openapi.json.md). Это инструкции, не копии машинных схем. Документация описывает реализованный контракт; доступность удалённых интерфейсов определяется live discovery, а не наличием этой инструкции.

## Pick method

Шаг 2 — выбрать способ доступа.

Публичное чтение каталога не требует регистрации или токена. Личные операции используют OAuth Authorization Code, PKCE **S256** и обязательный `resource`. Password grant, client credentials, refresh token и OIDC не поддерживаются. Browser WebMCP использует браузерную сессию и CSRF, а не REST Bearer token.

## Register

Шаг 3 — представить клиент.

Опубликуйте свой HTTPS Client ID Metadata Document (CIMD). Его URL — `client_id`. Динамического registration endpoint нет; не отправляйте запрос на выдуманный `/register`. Преднастроенный native CLI описан отдельно ниже.

Пример CIMD — замените домен и callback собственными HTTPS-адресами:

```json
{
  "client_id": "https://your-app.example/oauth/client.json",
  "client_name": "Your application",
  "redirect_uris": ["https://your-app.example/oauth/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
```

## Claim

Шаг 4 — идентифицировать приложение, не покупателя.

Отдельного claim endpoint или API key для доступа к покупателю нет; анонимный ключ каталога не заменяет его согласие. Сервер получает CIMD по HTTPS: `client_id` должен совпасть с URL документа, `redirect_uri` — точно с одним из опубликованных URI. CIMD — метаданные public client, не секрет и не аутентификация покупателя. PKCE связывает обмен кода с исходным verifier; сам по себе он не удостоверяет владельца домена или личность покупателя. Доступ к покупателю даёт только его согласие на Vesremont.

## Authorize

Шаг 5 — получить согласие покупателя.

Сгенерируйте случайные PKCE verifier и `state`; храните их на стороне клиента. `code_challenge = BASE64URL(SHA256(verifier))`, без padding. Откройте покупателю `/oauth/authorize?response_type=code&client_id=...&redirect_uri=...&scope=...&resource=...&state=...&code_challenge=...&code_challenge_method=S256`. Кодируйте каждое значение параметра отдельно.

Покупатель на Vesremont видит приложение, запрашиваемые права и получателя токена и сам разрешает доступ. Предварительная регистрация для гостевой корзины не обязательна. Вернувшись в callback, проверьте точное совпадение `state` и ожидаемый issuer, если он передан. При отказе не продолжайте личные операции.

## Exchange

Шаг 6 — обменять одноразовый код.

В течение 5 минут отправьте form-urlencoded POST на `/oauth/token`: `grant_type=authorization_code`, `code`, исходные `client_id`, `redirect_uri`, `resource`, `code_verifier`. Код одноразовый. Токен живёт до 15 минут, исходное разрешение — до 1 часа; ориентируйтесь на `expires_in`, а не на собственные предположения. Никогда не публикуйте verifier, code или token.

## Use

Шаг 7 — выполнить разрешённую операцию.

Передавайте токен только в `Authorization: Bearer …` по HTTPS. Не помещайте его в URL, логи или ссылки для покупателя. При истечении снова получите согласие; автоматического refresh нет. После неопределённого исхода записи перечитайте состояние, не повторяйте заказ с новым ключом.

`resource` выбирается отдельно для токена: `https://vesremont.com/api/v1`, `https://vesremont.com/mcp`, `https://vesremont.com/a2a` либо `https://vesremont.com/ucp/v1`. Используйте только опубликованные интерфейсы. Токен одного получателя не подходит другому; общее согласие позволяет получить отдельные токены описанным выше обменом. Запрашивайте минимальные scopes из OpenAPI/описания инструмента; UCP использует `dev.ucp.shopping.cart:manage` и `dev.ucp.shopping.checkout:manage`.

## Revocation

Шаг 8 — отозвать доступ.

Form-urlencoded POST `/oauth/revoke` с `token` и `client_id`. Отзыв исходного токена прекращает доступ и его производных. Покупатель может отозвать всё разрешение и связанные уведомления на [странице разрешений](https://vesremont.com/oauth/permissions). Выход из аккаунта, смена пользователя или потеря исходной браузерной сессии прекращают доступ к её данным. Клиент должен удалить отозванные токены; успешный revoke не разрешает дальнейшие операции.

## Errors

OAuth возвращает поле `error`; REST использует `application/problem+json` и `code`. Это разные контракты ошибок: не считайте любой HTTP 200 доказательством полученных прав. Проверяйте HTTP status, формат и обязательные поля ответа.

- При отказе покупателя (`access_denied`) остановите flow; новый запрос согласия возможен только по его действию.
- При неверном `state`, issuer или неожиданном callback отклоните ответ. Не обменивайте код и не продолжайте личные операции.
- При `invalid_grant` не повторяйте обмен одноразового кода: он мог истечь, быть использован или не соответствовать PKCE/resource. Начните новое разрешение покупателя.
- При REST 401 прекратите использование токена; при необходимости получите новое разрешение. При 403 проверьте scopes и resource, не подменяйте пользователя или cookies.
- При 429 соблюдайте `Retry-After`, если он передан. При сетевом сбое или 503 не считайте запись успешной: перечитайте состояние перед повтором; для заказа сохраняйте исходный Idempotency-Key.

Для поддержки сохраняйте только безопасный `request_id`, если он есть. Не записывайте authorization code, verifier, токены, полный callback URL, cookies или контакты покупателя в общие логи.

## Native CLI

Преднастроенный public client `vesremont-cli` использует loopback callback `http://127.0.0.1:{port}/callback` только на компьютере покупателя. Самостоятельному приложению не следует выдавать себя за этот клиент: HTTPS-интеграция использует собственный CIMD. CLI открывает Authorization Code + PKCE flow, проверяет state, обменивает код и отзывает токен при завершении команды. Встроенный CLI использует порт 8765; он должен быть свободен. HTTP допускается только для этого loopback callback, а API и OAuth endpoints остаются HTTPS.

## Browser consent и CSRF

Согласие на доступ, подтверждение заказа и UCP review обрабатывает сайт в текущем браузерном контексте. Страница публикует CSRF-токен, JavaScript передаёт его в изменяющем POST, сервер выполняет действующую проверку. PKCE и state не заменяют эту защиту. Внешний агент открывает страницу покупателю и не отправляет согласие или подтверждение вместо него; при отказе не продолжает личные операции.

Origin браузерного приложения должен быть явно разрешён оператором для CORS. CORS не заменяет OAuth. Серверный клиент не должен подделывать browser Origin. Наличие UCP profile, Agent Card или MCP descriptor также не даёт прав на покупателя.
- OAuth: not currently used for browser WebMCP

Отдельный stateless endpoint `GET https://vesremont.com/api/v1/cart-drafts/?product_id={id}` выдаёт только подписанную подборку ID товаров и количества. Добавьте `quantity` или существующий `draft`, чтобы увеличить подборку. Ответ содержит `share_url`; при открытии покупателем цены перепроверяются, а перенос в настоящую корзину выполняется браузерным POST с текущим CSRF-токеном. Ссылка не создаёт заказ и не заменяет OAuth/API checkout.

Browser WebMCP и удалённые интерфейсы не смешивают сессионные cookies и OAuth credentials. Текущий состав опубликованных интерфейсов указан отдельно в agent.json и API-документации.

Developer documentation: https://vesremont.com/developers/

Agent documentation: https://vesremont.com/agents/

Capability manifest: https://vesremont.com/agent.json

---

Title: Аутентификация Vesremont: browser session, CSRF и OAuth PKCE

Description: Авторизация browser WebMCP и REST/MCP/A2A/UCP: session, CSRF, PKCE S256, scopes, resource, отзыв разрешений и подтверждение заказа.

Canonical: https://vesremont.com/auth.md

Last modified: 2026-10-06
