Вход для сайтов
Логин на вашем сайте без своего бэкенда: обычный OIDC-клиент, форма входа на нашей стороне, способы входа переключаются тумблерами. Пароли, письма, коды и отпечатки — не ваша забота.
Что вы получаете
- Стандартный OpenID Connect: подойдёт любая библиотека, а не только наша.
- Способы входа — почта-ссылка, passkey, Яндекс ID, VK ID, Telegram — включаются в кабинете, без выкладки сайта.
- Раздел «Люди»: кто у вас есть и кто вернулся.
- Вебхуки: событие приезжает на ваш адрес само, опрашивать нас не нужно.
Пять шагов
- Заведите сайт в разделе Сайты. Сайт — это отдельная аудитория: вошедший на одном сайте на другом входит заново.
- Укажите адрес возврата и заберите ключ. Адрес вида
https://ваш-сайт/api/auth/callback. В ответ придутclient_idи секрет. - Включите способы входа тумблерами в карточке сайта. Способы, недоступные в вашей стране, мы не показываем.
- Вставьте вход в сайт. На Next.js — наш пакет и один роут. На остальных стеках — обычный authorization code flow.
- Подключите людей и события. В карточке сайта появятся разделы «Люди» и «События».
Переменные окружения
Имена переменных — часть контракта: кабинет, наш пакет и инструкция для агента называют их одинаково.
OIDC_ISSUER=https://id.zerno.one
OIDC_CLIENT_ID=<из карточки сайта>
OIDC_CLIENT_SECRET=<показан один раз при подключении>
OIDC_REDIRECT_URI=https://ваш-сайт/api/auth/callback
OIDC_SCOPE=<из карточки сайта, целиком>
ZERNO_SECRET=<сгенерируйте: openssl rand -base64 32>| Переменная | Что важно |
|---|---|
| OIDC_SCOPE | Копируйте целиком: в хвосте идентификатор вашей организации. Сократите до openid email profile — и форма покажет способы по умолчанию вместо ваших. |
| OIDC_REDIRECT_URI | Полный внешний адрес сайта, посимвольно совпадающий с зарегистрированным. За прокси не собирайте редиректы из адреса запроса: внутри контейнера это 0.0.0.0:3000. |
| ZERNO_SECRET | Ваш, не наш: им шифруется кука сессии на вашей стороне. Есть AUTH_SECRET — подойдёт он. |
Адреса провайдера
Канонический источник — discovery-документ, библиотеки читают его сами: https://id.zerno.one/.well-known/openid-configuration. Пути к ручкам у провайдера свои и на глаз не угадываются — прописывать их руками не нужно и не стоит.
Next.js
Пакет @zerno/next берёт на себя PKCE, сверку state, шифрование куки, обновление токена и защиту маршрутов. Роут — один, catch-all: он обслуживает и /api/auth/callback, и уже зарегистрированный /api/auth/callback/zerno.
// app/api/auth/[...zerno]/route.ts
import { zernoHandlers } from "@zerno/next";
export const { GET, POST } = zernoHandlers();Кнопки способов входа приходят с сервера — включили Яндекс тумблером, он появился на сайте без выкладки. Кто вошёл: на сервере await auth(), на клиенте useUser(). Куку не разбирайте — в ней шифром лежат токены провайдера.
Готовый рецепт с файлами лежит в кабинете, на странице «Как подключить», и там же — промпт, по которому подключение сделает ваш кодовый агент.
Другие стеки
Это обычный OIDC: Django + Authlib, Node + openid-client, PHP, ручная реализация на 30 строк — всё подходит. Готовые рецепты под эти стеки отдаёт та же страница в кабинете.
Вебхуки
События приезжают на ваш адрес с подписью и повторами, если обработчик лежал.
| Событие | Когда |
|---|---|
| user.created | Человек появился впервые — заводите свою строку пользователя. |
| session.created | Вход. Любым способом: почтой, отпечатком, через провайдера. |
| user.identifier_verified | Подтверждена почта — ей уже можно доверять. |
| user.merged | Две учётки оказались одним человеком: перевесьте связи на новый sub. |
Тело: { event, at, tenant_id, sub, application_id, provider }. Почты и телефона в событии нет намеренно — они не должны размножаться по чужим логам; нужны — спросите по sub.
Подпись — в заголовке X-Zerno-Signature: t=<время>,v1=<hmac>: HMAC-SHA256 от <t>.<сырое тело> секретом подписки. Проверяйте и подпись, и свежесть метки времени, и сравнивайте константным по времени сравнением. Готовый сниппет показывается при создании подписки.
Что важно знать заранее
sub— единственный стабильный идентификатор. Почта меняется, почта не идентификатор.- Форма входа живёт на нашем домене. Общий домен с вашим сайтом был бы лишней поверхностью для атак.
- Профиль пользователя ведёт ваш сайт. Мы отвечаем за вход, а не за личный кабинет ваших клиентов.
- Адресов возврата может быть несколько — заведите отдельный для локальной разработки.
- Своей формы с логином и паролем быть не должно. Вход целиком на стороне провайдера; две библиотеки входа в одном проекте — два источника сессии, которых не бывает.
- Выход — только POST. Ссылкой он срабатывает от предзагрузчика браузера и от чужой страницы с
<img src>. - Проверяйте вход в dev-сборке. У продовой кука уходит с флагом Secure, и по
http://localhostбраузер её не сохранит — вход будет выглядеть сломанным на ровном месте.
Сколько стоит
Тариф считает активных пользователей: до порога — бесплатно, дальше — за пользователя сверх него. Цифры на странице тарифов, механика списаний — в разделе Учётная запись и счёт.
