Доступ
Кто может вызывать API и как фронтенд это доказывает. Три ситуации в том порядке, в каком вы с ними встретитесь.
1. Браузер на сайте (куки + CSRF)
По умолчанию. Тема или любая страница на домене вашего сайта входит в систему, и Pano ставит куки. Куки Pano привязаны только к хосту и имеют SameSite=Lax.
Каждая запись (POST, PUT, PATCH, DELETE) вошедшей cookie-сессии должна нести токен CSRF:
curl -s -b jar -c jar http://localhost:8088/api/v1/auth/csrf
# {"csrfToken":"..."}await fetch('/api/plugins/pano-plugin-market/cart/items', {
method: 'POST',
credentials: 'include',
headers: { 'content-type': 'application/json', 'X-CSRF-Token': token },
body: JSON.stringify({ productId: 1 })
});Отсутствующий или неверный токен даёт 403 INVALID_CSRF_TOKEN. @panomc/sdk и типизированный клиент получают и отправляют токен за вас. Если никто не вошёл, GET /auth/csrf отвечает 401: это не ошибка, просто сначала войдите.
2. Браузер на другом адресе (разрешённые источники)
Страница на https://play.example.com вызывает Pano на https://example.com: добавьте источник страницы в панели в Внешний вид -> Темы -> кнопка шестерёнки (Настройки показа сайта): включите «Разрешить другим веб-сайтам доступ к этому Pano», откроется список «Веб-сайты, которым разрешён доступ к этому Pano» (или PUT /api/v1/panel/frontend/origins, не больше 20).
- Формат
scheme://host[:port], без пути и без подстановочных знаков.https, если это неlocalhostи не IP. - Он должен иметь тот же регистрируемый домен, что и
website-url. Другой домен отклоняется (ORIGIN_DIFFERENT_SITE): используйте ключ фронтенда. - Разрешённые источники получают заголовки CORS (
credentials: 'include', никогда*). Preflight отвечает204. - Браузеры могут отправлять
content-type,accept,x-csrf-token,x-requested-with. ЗаголовокAuthorizationиз браузера не отправляется. - Любой другой источник не получает заголовков CORS на чтение и получает
403 ORIGIN_NOT_ALLOWEDна запись.
3. Сервер (ключ фронтенда + токен сессии)
BFF, собственный фронтенд или любой сервер, который вызывает Pano от имени многих посетителей, использует ключ фронтенда (в панели: «Ключ подключения сайта»). Создайте его в Внешний вид -> Темы -> кнопка шестерёнки (Настройки показа сайта) -> Ключи подключения сайта -> Настроить. Он показывается один раз вместе с двумя строками .env:
PANO_API_URL=https://example.com/api
PANO_FRONTEND_KEY=pfk_...| Заголовок | Значение |
|---|---|
X-Pano-Frontend-Key: pfk_... | Определяет ваш сервер. Неверный ключ: 401 INVALID_FRONTEND_KEY. |
X-Pano-Client-Ip: 203.0.113.9 | Адрес посетителя. Учитывается только с действительным ключом; иначе 400 INVALID_CLIENT_IP. |
Authorization: Bearer <sessionToken> | Сессия посетителя, из ответа на вход. |
Ключи работают только пока режим фронтенда не «Тема» (строка «Ключи подключения сайта» не показывается, пока выбрано «Тема»): в режиме «Тема» ключ создать нельзя, а запрос с сохранённым ключом получает 403 FRONTEND_ACCESS_DISABLED. Сохранённые ключи снова работают в режимах CUSTOM_APP, EXTERNAL и NONE. Разрешённые источники работают в любом режиме.
Ключ пропускает проверку источника и имеет собственные корзины ограничения частоты на каждый адрес посетителя. Правила пароля, капчи и 2FA работают без изменений.
Вход с ключом отвечает токеном в теле вместо куки:
curl -s http://localhost:8088/api/v1/auth/login \
-H 'X-Pano-Frontend-Key: pfk_...' -H 'X-Pano-Client-Ip: 203.0.113.9' \
-H 'content-type: application/json' \
-d '{"usernameOrEmail":"steve","password":"secret"}'
# {"sessionToken":"eyJ...","expiresAt":1790000000000}Дальше отправляйте этот токен как Bearer. Токен CSRF не нужен. Храните его в HttpOnly-куки вашего сайта.
Токен это сессия сайта: он никогда не работает с API панели (403 SITE_TOKEN_NOT_ALLOWED) и никогда как куки. Выход, бан или смена пароля завершают его, как любую сессию.
Билет WebSocket
Браузеры не могут задать заголовки для WebSocket. Запросите одноразовый билет (30 секунд) и поместите его в URL:
curl -s -X POST http://localhost:8088/api/v1/auth/ws-ticket -b jar -H 'X-CSRF-Token: ...'
# {"ticket":"...","expiresIn":30}Откройте сокет с ?ticket=<ticket>. Использованный или просроченный билет даёт 401 INVALID_WS_TICKET. BFF получает билет по Bearer-токену посетителя и передаёт его браузеру.
За обратным прокси
Pano берёт адрес посетителя из X-Forwarded-For только от доверенного узла (server.trusted-proxies, а также loopback и частные адреса). Если все посетители выглядят как один адрес, панель показывает баннер со строкой, которую нужно добавить.