Сообщение об ошибке
Unexpected registration response origin "http://localhost:3000", expected "https://app.example.com"
Также выводится как
- Unexpected authentication response origin "http://localhost:3000", expected "https://app.example.com"
- Unexpected registration response challenge "b3RoZXI", expected "c2VydmVyLWNoYWxsZW5nZS0xMjM"
Первое значение в кавычках — источник, о котором сообщил браузер, второе — значение из вашей конфигурации. При входе в тексте стоит «authentication response»; при несовпадении challenge та же библиотека выдаёт «Unexpected registration response challenge …».
SimpleWebAuthn (@simplewebauthn/server) — verifyRegistrationResponse / verifyAuthenticationResponse
Что это означает
Браузер записал источник (origin) страницы в `clientDataJSON` (данные, подписываемые аутентификатором), и он отличается от `expectedOrigin`, переданного вашим сервером в SimpleWebAuthn. Эта проверка нужна, чтобы церемонию с учётными данными, выполненную на одном сайте (например, на фишинговом двойнике), не принял другой сайт. С аутентификатором всё в порядке: представление сервера о том, где находятся его страницы, не совпадает с тем, где на самом деле прошла церемония.
Почему это происходит
Источник продакшена указан в среде разработки (или наоборот)
частоВ `expectedOrigin` жёстко прописан URL продакшена, а тестирование идёт на `http://localhost:3000`, в превью-развёртывании или по URL туннеля (ngrok, Cloudflare Tunnel).
Страница входа доступна из нескольких источников
часто`www.` и домен без этого префикса (apex), несколько национальных доменов или отдельный поддомен `accounts.`. Каждый источник, на котором может пройти церемония, должен быть в списке.
Нативное приложение Android
иногдаCredential Manager сообщает в качестве источника `android:apk-key-hash:…`, а не веб-источник вашего сайта. Хеш ключа подписи приложения должен быть в `expectedOrigin`.
Как исправить
- 1.
Явно перечислите все легитимные источники
`expectedOrigin` принимает массив. Указывайте в нём только источники, которыми вы управляете; никогда не берите источник из самого запроса.
js const verification = await verifyRegistrationResponse({ response: body, expectedChallenge: session.currentChallenge, // Every origin that legitimately hosts your sign-up page, exactly as browsers report it: expectedOrigin: ["https://app.example.com", "https://www.example.com"], expectedRPID: "example.com", }); - 2.
Добавьте источник приложения Android
Вычислите SHA-256 сертификата подписи приложения в кодировке base64url и добавьте его в форме `android:apk-key-hash:`.
js // Android apps report "android:apk-key-hash:<base64url SHA-256 of the app signing certificate>". // SHA-256 fingerprint from Play Console or: keytool -list -v -keystore release.jks | grep SHA256 const fingerprintHex = process.env.ANDROID_SIGNING_CERT_SHA256.replaceAll(":", ""); const androidOrigin = "android:apk-key-hash:" + Buffer.from(fingerprintHex, "hex").toString("base64url"); const verification = await verifyRegistrationResponse({ response: body, expectedChallenge: session.currentChallenge, expectedOrigin: ["https://example.com", androidOrigin], expectedRPID: "example.com", });