Перейти к основному содержимому

Где открывать страницу входа

Поток один и тот же в любом контейнере: код всегда приходит на ваш redirect_uri, и обменять его может только ваш сервер. Браузер — вкладка, всплывающее окно или WebView — лишь место, где пользователь входит.

КонтейнерСтатусКак
Переход в той же вкладкеподдерживаетсяСамый простой вариант: перенаправьте на /authorize, получите пользователя на redirect_uri.
Всплывающее окноподдерживаетсяВаша страница остаётся открытой. Рецепт ниже.
Мобильный WebView (WKWebView / Android WebView)поддерживаетсяОткройте /authorize в WebView и перехватите переход на redirect_uri. Режим embed=1 без подвала — см. параметры ниже.
<iframe> на вашем сайтезапрещеноСтраница входа не встраивается в чужие сайты — это защита от кликджекинга. Используйте всплывающее окно: в нём видна адресная строка, а это и делает страницу входа заслуживающей доверия.

Всплывающее окно​

Открывайте окно прямо в обработчике клика (окно, открытое позже, заблокирует браузер) и ведите его на /authorize, а не на страницу входа напрямую — именно /authorize создаёт запрос на вход.

const POPUP = 'width=460,height=720,menubar=no,toolbar=no,location=yes'

button.addEventListener('click', async () => {
// ваш сервер строит ссылку /authorize: у него client_id, redirect_uri и state, который он потом проверит
const { url } = await fetch('/auth/start', { method: 'POST' }).then((r) => r.json())
const popup = window.open(url, 'verificahub-id', POPUP)

window.addEventListener('message', (e) => {
if (e.origin !== 'https://id.verificahub.ru') return // всегда проверяйте origin
if (e.data?.source !== 'verificahub-id') return
if (e.data.type === 'verified') showSpinner() // только сигнал интерфейсу — см. ниже
})
})

Потом окно переходит на ваш redirect_uri, и закрывает его ваша страница callback:

<!-- ваш /auth/callback, открытый в окне после успешного обмена кода на сервере -->
<script>
window.opener?.postMessage({ type: 'login-complete' }, window.location.origin)
window.close()
</script>

Учитывайте, что пользователь может закрыть окно сам: проверяйте popup.closed или просто оставьте кнопку входа активной.

Сообщение verified​

Перед возвратом страница отправляет в window.opener (и window.parent):

{ source: 'verificahub-id', type: 'verified' }

В нём нет ни кода, ни номера: сообщение уходит с targetOrigin: '*', и всё, что в нём есть, могло бы прочитать любое окно в цепочке. Это сигнал интерфейсу («уберите кнопку, покажите загрузку»), а не результат. Результат — то, что ваш сервер получил от /exchange.

Параметры страницы​

Передайте их в /authorize — он перенесёт их на страницу входа. Значения вне списка отбрасываются.

Параметр /authorizeЧто делает
ui_locales=ru|en (или lang)Язык страницы. По умолчанию — defaultLocale клиента, иначе русский.
theme=light|darkЗакрепляет тему — для приложений, чья тема не совпадает с системной. По умолчанию страница следует теме устройства.
display=touch (или embed=1)Без подвала — для WebView, где рамку рисует приложение.

Размеры​

  • Всплывающее окно: удобно 460 × 720, работает от ~360 × 560. В низком окне страница уплотняется, а если не помещается — прокручивается.
  • WebView: дайте хотя бы 360 px ширины. Поля ввода не меньше 16 px, поэтому iOS не увеличивает страницу при фокусе; вырезы экрана и полоса «домой» учитываются.
  • Учитываются prefers-reduced-motion и режим высокой контрастности Windows.

Что гарантирует страница​

  • Не видит ваш client_secret и не меняет код на номер — это делает только ваш сервер.
  • Без cookies: доступ даёт одноразовая ссылка, поэтому потерянная ссылка просто истекает.
  • Номер на странице хранится только в этой вкладке и только до конца входа — чтобы после перезагрузки можно было продолжить с ввода кода.
  • Заголовки: HSTS, Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff; встраивание в чужие сайты запрещено; страница входа закрыта от индексации.