Skip to main content
Виджет — ваша страница, которую мы открываем внутри кабинета Ai-Seven: например, рейтинг с подсвеченной строкой пользователя и кнопкой «Купить участие». Адрес виджета и название пункта меню мы заводим в настройках вашей интеграции. Чтобы вы знали, кто открыл виджет, мы добавляем к адресу подписанный нами токен с public_id пользователя. Вы проверяете подпись нашим публичным ключом — так вы знаете, что ссылку сформировали мы и public_id не подменён. Но токен предъявительский: подпись не доказывает, что ссылку открыл тот, кому мы её выдали, — см. Кто открыл ссылку.

Подключение

1

Вы присылаете адрес виджета и название пункта меню

Адрес страницы виджета — только https:// и на вашем домене, не на домене Ai-Seven. Название пункта меню в кабинете Ai-Seven — до 100 символов.
2

Мы сообщаем значение aud

Это ваш iss в Partner API — идентификатор вашей интеграции. С ним вы сверяете aud каждого токена.

Как это работает

1

Пользователь открывает страницу виджета

В кабинете Ai-Seven — пункт меню с названием вашего виджета.
2

Даёт согласие

При первом открытии — на передачу данных вам, см. Согласие и профиль. Без согласия ссылка с токеном не выдаётся, и public_id к вам не уходит.
3

Мы открываем виджет с токеном

По адресу <адрес виджета>?token=<JWT> — во фрейме кабинета или, по кнопке «Открыть в новой вкладке», в отдельной вкладке. Токен выпускается заново при каждом открытии.
4

Вы проверяете токен

Подпись, срок, адресата и одноразовость — и обмениваете токен на свою сессию. Токен срабатывает у любого, у кого окажется ссылка, поэтому показывайте пользователю, под кем он вошёл.

Ссылка

Токен передаётся в параметре token. Если в адресе виджета уже есть параметры, токен добавляется к ним через &: https://partner.example.com/widget?lang=ru&token=....

Формат токена

Заголовок

string
required
Всегда RS256.
string
required
Идентификатор нашего ключа подписи. По нему вы выбираете ключ из JWKS.

Payload

string
required
Всегда https://aisevenai.ru.
string
required
Ваш iss в Partner API — идентификатор вашей интеграции, который мы сообщаем при подключении. Токен, выпущенный для другого партнёра, вам не подходит.
string
required
public_id пользователя — тот же, что в статистике. По нему вы выдаёте подписку и доступы.
integer
required
Момент выпуска, unix-время в секундах.
integer
required
Момент истечения, unix-время в секундах. Токен живёт 300 секунд.
string
required
Уникальный идентификатор токена. Нужен для одноразовости.

JWKS

Наши публичные ключи — по фиксированному адресу, без авторизации:
Это пример формата. Ключ берите только запросом по адресу выше, в код не вставляйте.
Пример

Проверка токена на вашей стороне

1

Подпись — только RS256

Список допустимых алгоритмов задайте жёстко: ["RS256"]. Не берите алгоритм из заголовка токена.
2

Ключ — только из нашего JWKS

Загружайте JWKS только с фиксированного адреса https://aisevenai.ru/api/v1/partner/.well-known/jwks.json. Не стройте адрес ключей из iss, jku или x5u токена.
3

iss и aud — точное совпадение

iss — точно https://aisevenai.ru, aud — точно ваш iss в Partner API.
4

Срок

Проверяйте exp и iat с допуском на расхождение часов 30–60 секунд. Токен живёт 300 секунд.
5

Кеш JWKS и незнакомый kid

Держите JWKS в кеше не дольше 10 минут. Если kid нет в закешированном JWKS — перечитайте JWKS, но не чаще раза в минуту.
6

Одноразовость по jti

Храните jti принятого токена до его exp плюс допуск по часам и записывайте атомарно — например, в Redis: SET widget-jti:<jti> 1 NX EXAT <exp+60>. Не записалось — токен уже предъявляли: отклоните его.
7

Сразу обменяйте токен на свою сессию

Главный способ — обмен на сервере. Ваш сервер принимает GET <адрес виджета>?token=…, проверяет токен, ставит сессию и отвечает 303 на тот же адрес без token. Так токен не видят скрипты страницы и он не уходит в Referer.Одностраничное приложение, которое получает токен в браузере, отправляет его на свой сервер в теле POST, а не в адресе, и сразу убирает из адреса только параметр token, сохраняя остальные:
sub (public_id) — не доказательство входа сам по себе. Опознавайте пользователя только по проверенному токену, а не по public_id из адреса, формы или вашей базы.

Кто открыл ссылку

Токен виджета — предъявительский. Подпись доказывает, что ссылку выпустили мы для этого public_id, но не то, что её открыл тот, кому мы её выдали: пересланная или перехваченная ссылка до exp сработает и у другого человека. Поэтому:
  • Показывайте на странице виджета, под кем выполнен вход.
  • Если в браузере уже открыта ваша сессия другого sub, не переключайте её молча: покажите, что пришёл вход под другим пользователем, и спросите, под кем продолжить.
  • При входе по ссылке в отдельной вкладке, а не во фрейме, просите подтверждение: «Продолжить как <имя>». На сервере отличайте фрейм от вкладки по заголовку Sec-Fetch-Dest (iframe или document); в браузере — по window.self === window.top.
  • Проверка Referer — только дополнение к этим мерам, а не замена им.

Пример

remember_jti_or_reject / rememberJtiOrReject — ваше хранилище принятых jti с атомарной записью: например, SET widget-jti:<jti> 1 NX EXAT <until> в Redis. Команда ничего не записала — токен уже предъявляли, отклоните его.

Ротация ключа

Мы меняем ключ подписи на новый, с новым kid, по шагам:
1

Новый ключ появляется в JWKS

Заранее и вторым в списке — токены им ещё не подписываются.
2

Новый ключ начинает подписывать

Через несколько минут новые токены подписываются уже им. Старый ключ остаётся в JWKS.
3

Старый ключ убирается из JWKS

Не раньше чем через 300 секунд — срок жизни токена — плюс время кеша JWKS на вашей стороне.
Держите в кеше все ключи из JWKS, а не один. Тогда во время ротации проходят токены и со старым, и с новым kid. Пришёл токен с незнакомым kid — перечитайте JWKS, но не чаще раза в минуту.
При компрометации ключ убирается из JWKS сразу, без шагов выше. Не кешируйте JWKS дольше 10 минут, не вшивайте ключи в код и конфиги.

Требования к странице виджета

Виджет открывается во фрейме внутри кабинета Ai-Seven, а по кнопке «Открыть в новой вкладке» — в отдельной вкладке, со свежим токеном. Страница должна работать в обоих случаях, и так, чтобы токен не утёк:
Отдавайте страницу виджета с заголовком Content-Security-Policy: frame-ancestors https://aisevenai.ru. Заголовок X-Frame-Options: DENY или SAMEORIGIN встраивание запретит.
Фрейм открывается с атрибутом sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox" — других разрешений нет.Навигация верхнего окна запрещена (нет allow-top-navigation): виджет не может увести вкладку Ai-Seven на свой адрес. Оплату, вход и любые свои окна открывайте новой вкладкой — ссылкой с target="_blank" или window.open.Диалоги браузера (alert, confirm, prompt) и скачивание файлов во фрейме не работают. Вместо диалогов показывайте свои окна на странице, файлы открывайте новой вкладкой.
Отдавайте страницу виджета с заголовком Referrer-Policy: no-referrer — иначе адрес с токеном уйдёт в Referer запросов к сторонним ресурсам страницы.
Лучше всего — обмен на сервере с ответом 303 на тот же адрес без token. В одностраничном приложении — отправка токена на свой сервер в теле POST и history.replaceState, как в проверке токена. Обновление страницы не должно требовать токена.
Не пишите строку запроса страницы виджета никуда, где её прочтут: в журналы веб-сервера, балансировщика, CDN и WAF, в трекеры ошибок (Sentry и подобные), в APM и системы аналитики. Если счётчик аналитики загружается на странице виджета, он должен стартовать после того, как токен убран из адреса.