AACsearch
Диагностика

Ошибки авторизации

Ответы 401 / 403 от API AACsearch — диагностика отсутствующих заголовков, неверного префикса ключа, отозванных ключей, истечения scoped-токенов и ограничений по origin.

СимптомВероятная причина
401 missing_bearer_tokenЗаголовок Authorization отсутствует или искажён
401 unauthorizedПустой или неверный формат токена
401 token_expiredTTL scoped-токена истёк
403 forbiddenНеверный префикс ключа для операции, или ключ отозван
403 invalid_or_revoked_keyConnector-токен отозван или не совпал
403 origin_not_allowedOrigin запроса из браузера не входит в allowedOrigins
403 key_does_not_match_indexКлюч был выпущен для другого indexSlug

Дерево решений

401 missing_bearer_token
  → проверьте, что Authorization задан и начинается с `Bearer `

401 unauthorized
  → формат токена неправильный; убедитесь что он начинается с
    ss_search_, ss_connector_, ss_scoped_ или aa_admin_ и без пробелов

401 token_expired (только scoped-токены)
  → истёк exp scoped-токена; выпустите новый на сервере

403 forbidden / invalid_or_revoked_key
  → проверьте Search → API Keys; не отозван ли?
  → если не отозван: префикс не совпадает — см. таблицу ниже

403 origin_not_allowed
  → заголовок Origin запроса не входит в allowedOrigins[] ключа
  → в dev оставьте allowedOrigins пустым

Подберите правильный ключ под операцию

ОперацияТребуемый префикс
POST /api/search/...ss_search_* или ss_scoped_*
POST /api/multi-searchss_search_* или ss_scoped_*
POST /api/connectors/handshake и прочие /api/connectors/...ss_connector_*
POST /api/projects/{projectId}/sync/...ss_connector_*
POST /api/webhooks/sync/{indexSlug}HMAC-SHA256 подпись секретом вебхука индекса (без Bearer)
POST /api/v1/... (управление)aa_admin_* (только сервер)

ss_search_* ключ на connector-эндпоинте или ss_connector_* ключ на /api/search — оба возвращают 403 forbidden. Маппинг проверяется функциями gatePublicSearchRequest() и gateConnectorRequest() в packages/api/modules/search/lib/.

Проверки

  1. Заголовок Authorization корректен.

    curl -i -X POST https://app.aacsearch.com/api/search/products \
      -H "Authorization: Bearer ss_search_..." \
      -H "Content-Type: application/json" \
      -d '{ "q": "test" }'

    Ожидайте 200. Если 401 — скопируйте поле error из ответа в таблицу выше.

  2. Ключ не отозван. Откройте Search → API Keys. Отозванные ключи скрыты в списке по умолчанию — переключите "Show revoked" чтобы увидеть.

  3. Origin разрешён (только браузер). Dev tools → Network → кликните на падающий запрос → проверьте заголовок Origin. Он должен совпадать с одной из записей в allowedOrigins[] ключа.

    // Получить список разрешённых origin программно:
    const keys = await admin.listKeys();
    const yours = keys.find((k) => k.id === "key_...");
    console.log(yours.allowedOrigins);
  4. Scoped-токен не истёк. Декодируйте payload (он base64url, не зашифрован) и проверьте expiresAt:

    const [, payloadB64] = scopedToken.replace("ss_scoped_", "").split(".");
    const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
    console.log(payload.expiresAt, "vs now", Math.floor(Date.now() / 1000));
  5. Slug индекса совпадает. Ключ для indexSlug: "products" не может искать в indexSlug: "categories". Пересоздайте ключ под нужный индекс или не указывайте slug при выпуске чтобы открыть все индексы организации.

Исправление

ДиагнозЧто сделать
Нет заголовкаДобавьте Authorization: Bearer <ключ>
Неверный префиксВыпустите ключ нужного типа — см. таблицу выше
Ключ отозванСоздайте замену: Search → API Keys → Create key
Origin запрещёнДобавьте origin сторфронта в allowedOrigins[] (или оставьте пустым в dev)
Scoped-токен истёкВыпустите свежий scoped-токен на сервере; клиент должен запросить новый при 401 token_expired
Slug не совпадаетПересоздайте ключ без привязки к индексу или с правильным slug

Восстановление на стороне браузера

Для интеграций виджетного типа — обрабатывайте 401 token_expired как сигнал перезапросить:

import { AacSearchClient, AacSearchError } from "@repo/search-client";

async function searchWithTokenRefresh(query: string) {
	try {
		return await client.search({ q: query });
	} catch (err) {
		if (err instanceof AacSearchError && err.code === "token_expired") {
			const fresh = await fetch("/api/search-token").then((r) => r.text());
			client.setApiKey(fresh);
			return await client.search({ q: query });
		}
		throw err;
	}
}

На сервере — подключите Server Action (или роут /api/search-token), вызывающий orpc.search.createScopedToken.call({ ... }) со свежим expiresInSeconds.

Пакет диагностики

ПолеЗаметки
ID организацииобязательно
Время (UTC)обязательно
Request IDиз заголовка ответа X-Request-Id
Префикс упавшего ключатолько первые 12 символов — никогда не весь ключ
Origin (если браузер)из заголовков запроса
Эндпоинтполный путь, например POST /api/search/products
Тело ответа{ "error": "...", "message": "..." }

Никогда не вставляйте полный API-ключ в тикет. Префикса и последних 4 символов хватает поддержке для идентификации в audit log.

Связанные

On this page