Skip to main content
Veriqa встраивается в ваш ASP.NET Core-хост и работает как auth-сервер OpenIddict: поднимает OIDC-эндпоинты, страницу входа с QR и каналами, движок транзакций и адаптеры мессенджеров. Отдельный процесс или Docker не нужны.
Хотите вовсе не тянуть Veriqa в свою сборку? Тот же сервер работает и отдельно, а приложение общается с ним по обычному OIDC — в облаке, в Docker или службой ОС; ни одного пакета Veriqa в дереве зависимостей и в SBOM. Что это меняет для .NET-клиента — если Veriqa работает отдельно.
В примерах — плейсхолдеры (my-app, токены ботов). Подставьте свои значения и никогда не коммитьте реальные секреты: держите их в user-secrets в разработке и в секрет-сторе в продакшне.
Готовый runnable-пример — samples/dotnet/inproc/login/ в репозитории; его адрес берите на veriqa.app/source. Пример можно склонировать и взять за основу вместо ручной обвязки.

1. Установите пакеты

Нужен один канал — ставьте только его пакет. Каждый канал поставляется отдельно и приносит только свой SDK: Veriqa.Core.ChannelAdapter.Telegram, .WhatsApp, .Max, .Email. Тогда установке, которая пускает пользователей через Telegram, не приезжает почтовая библиотека. Пакет Veriqa.Core.ChannelAdapter приходит с любым из этих пакетов — отдельно его ставить не нужно. Поставить и метапакет, и отдельный канал — штатно: NuGet сведёт к одной копии.

2. Зарегистрируйте сервисы

AddVeriqaAuthServer читает конфигурацию из стандартного IConfiguration хоста. Внутри вы выбираете хранилище OpenIddict, настраиваете хранилище транзакций и подключаете нужные каналы.
Program.cs
У хранилища OpenIddict нет умолчания. Хост, не вызвавший ни UseInMemoryOpenIddictStore(), ни UseOpenIddictDatabase(...), вне окружения Development не стартует: проверка на старте останавливает приложение и называет лечение. В Development тот же текст пишется как Warning, и хост поднимается. Явный InMemory стартует в любом окружении, но на каждом старте предупреждает о волатильности: клиенты, токены и authorizations не переживают рестарт и не разделяются между репликами.
Вне Development обязательны и сертификаты токенов. Это ключи OpenIddict, которыми сервер подписывает и шифрует id_token / access_token, — не HTTPS-сертификат. Без Veriqa:OpenIddict:Server:SigningCertificatePath (или …SigningCertificateBase64) и парного ключа EncryptionCertificate… хост не стартует; в Development вместо них генерируются dev-ключи. Ключи — в справочнике конфигурации.
Подключайте только те каналы, что реально используете. Каждый адаптер активируется, только если его секция в конфигурации включена ("Enabled": true) — иначе регистрируются лишь опции и валидатор.

3. Соберите middleware pipeline

Program.cs
Эндпоинты Email-канала (magic link и письмо в один тап) MapVeriqaAuthServer мапит автоматически — но только когда канал включён в конфигурации (Veriqa:Channels:Email:Enabled: true). Отдельный вызов не нужен.
Каталог wwwroot/locales необязателен: без него сообщения каналов уходят на базовом языке, и предупреждение на старте Locale files directory not found … ожидаемо. Переводить вручную ничего не нужно: готовые локали (en, ru, zh, pseudo) поставляются пакетом Veriqa.Core.ChannelLocales — как их подключить, описано в Локализации. Так же ожидаемо на дефолтной конфигурации Initiator context geolocation is enabled but the GeoIP provider is unavailable: вход работает, просто гео-поля контекста инициатора остаются пустыми. В Development после старта идут ещё несколько десятков диагностических строк info о разрешённой конфигурации — это нормально.

4. Настройте конфигурацию

Секреты каналов и OIDC-клиенты задаются в конфигурации. Всё, что принадлежит Veriqa, живёт под корневым ключом Veriqa: секции каналов — под Veriqa:Channels, настройки auth-сервера — рядом с ними в том же разделе.
appsettings.json
Замените плейсхолдеры до первого запуска. YOUR_BOT_TOKEN и ему подобные — не значения, с которыми стартуют. Telegram и MAX в режиме UpdateMode: Webhook — том, что показан выше — регистрируют вебхук при старте, поэтому с плейсхолдером токена хост не запустится. У WhatsApp случай тише: при "Enabled": true его креды проверяются на старте только на наличие — пропущенное или пустое значение старт как раз валит, а строка-плейсхолдер проходит, — поэтому хост поднимается чисто, а негодное значение вскрывается только при отправке первого подтверждения. Настоящие креды берутся в Настройке каналов — поэтому выше "Enabled": true стоит ровно у одного канала. Остальные включайте, когда получите их креды.
Две последние секции — то, что пользователь читает по завершении входа. Channels:OutcomeNotice:DisplayIntent задаёт, где появляется квитанция исхода: ReplacePrompt — поставочное значение, выписанное здесь явно, чтобы настройка была видна, — ставит квитанцию на место сообщения с вопросом, а NewMessage оставляет вопрос в переписке и добавляет отдельное сообщение. Это намерение, а не обещание: Telegram и MAX его исполняют, WhatsApp всегда шлёт новое сообщение, а Email квитанцию исхода не показывает вовсе.MessageTemplates задаёт формулировки этих квитанций. Продукт везёт свои по адресу ByType:login:BySurface:…, а уровень перебирает все шаги адреса прежде, чем резолюция спустится на уровень ниже, — поэтому переобъявление достигается только по тому же адресу, отсюда и глубина Templates выше. {app} — серверный слот: для входа это DisplayName клиента. Последний шаг каждой лестницы не пользуется слотами, потому что ни один слот квитанции не гарантирован. Полные правила — в Справочнике конфигурации.
WhatsApp работает через официальный Meta Cloud API (Provider: MetaCloudApi); BusinessPhoneNumber — в формате E.164 для deep link. Подключайте только используемые каналы — секция без "Enabled": true канал не активирует, поэтому две секции выше показаны выключенными, а не убраны. Полная настройка Email (SMTP для Pull и inbound для Push) — в Настройке каналов.
InboundVerification — обязательный ключ каждой канальной секции: он объявляет, как проверяется подлинность входящего события канала. Включённый канал без него не даст хосту стартовать. Значения выше — для показанных режимов (UpdateMode: Webhook); в режиме Polling ставится outbound_fetch. Таблица по всем каналам и режимам — в Настройке каналов.
Запускаете на localhost? Переключите канал в Polling. Секции Telegram и MAX выше показаны в режиме Webhook, которому нужен публичный HTTPS-адрес: хост регистрирует вебхук на старте, и Telegram должен до него достучаться. До https://localhost:7300 — адреса, на котором идёт контрольная точка в конце этой страницы, — он не достучится. Polling публичного URL не требует, потому что Veriqa забирает апдейты сама:
Оба ключа меняются вместе: outbound_fetch — это значение, идущее с Polling, а shared_secret в нём даёт Warning на старте. WebhookBaseUrl и WebhookSecretToken в этом режиме не используются, их можно не задавать. Годится, чтобы дойти до рабочего входа до того, как появились домен и прокси, но не для развёрнутой установки — да и там один опрашивающий на бота (запуск более одного инстанса).
redirect_uri сверяется с AllowedRedirectUris точным совпадением — wildcard не поддерживаются. Валидатор при старте требует абсолютные URI; в продакшне используйте https (http — только для loopback-адреса, например localhost или 127.0.0.1, в разработке).

5. Подключите клиентское приложение

Всё, что выше, — сторона issuer’а. Клиент, который в него входит, — обычный OIDC Relying Party: ничего специфичного для Veriqa в нём нет, это та же обвязка, что вы написали бы для любого OpenID Connect провайдера. В ASP.NET Core эта обвязка приезжает одним пакетом:
Program.cs (клиентское приложение)
Неаутентифицированный визит отправляется на вход обычным Challenge — на страницу Veriqa с QR и кнопками каналов:
Program.cs (клиентское приложение)
Три значения должны сойтись с конфигурацией из шага 4. Симптом у каждого рассинхрона свой — по нему и опознаётся причина: dotnet new web выдаёт каждому проекту случайные порты, поэтому из коробки ни Authority, ни redirect_uri не указывают на запущенный процесс — шаг 10 поднимает оба на адресах, использованных здесь.
Клиент my-app из шага 4 задан без ClientSecret — значит, он публичный, и Veriqa требует от него PKCE (S256). Поэтому UsePkce = true, а ClientSecret не задаётся. Для конфиденциального клиента секрет прописывается с обеих сторон.
Запускаемая пара «issuer + клиент» лежит в репозитории (veriqa.app/source): samples/dotnet/inproc/login (хост с Veriqa, порт 7300) и samples/dotnet/inproc/login-client (это приложение, порт 7020). Они сходятся по конфигурации из коробки — запустите оба и пройдите вход. Чтобы вход дошёл до конца, на issuer’е нужен включённый канал с кредами: сэмпл поставляется со всеми каналами "Enabled": false, и без единого включённого страница входа отвечает no_channels_available.
Veriqa не выставляет end-session-эндпоинт, поэтому federated sign-out не поддерживается: выход на стороне клиента гасит его собственную cookie-сессию, но не сессию issuer’а.

6. Разберитесь в потоке входа

Пользователь начинает транзакцию на десктопе и подтверждает её в доверенном канале на телефоне. Veriqa отслеживает транзакцию и только потом отдаёт выпуск результата OpenIddict.

7. Эндпоинты

MapVeriqaAuthServer мапит стандартный OIDC-контракт и служебные эндпоинты: Эндпоинты Email-канала мапятся тем же MapVeriqaAuthServer автоматически — только при включённом канале (Veriqa:Channels:Email:Enabled: true):

8. Claims пользователя

Итоговый набор claims формирует маппер по умолчанию из завершённой транзакции: picture приходит, только когда выполнены все три условия: клиент запрашивает scope avatar (options.Scope.Add("avatar")), scope разрешён в AllowedScopes клиента и включён GetClaimsFromUserInfoEndpoint = true — в id_token аватар не попадает никогда, его отдаёт только userinfo. Как его забрать, не раздувая cookie, — см. OIDC + Veriqa: разбор. Маппинг переопределяется своим IClaimsMapper — см. Настройку и кастомизацию.

9. Выбор канала через acr_values

Клиент может ограничить вход конкретным каналом через стандартный OIDC-параметр acr_values формата channel:{тип}:
Несколько значений (channel:telegram channel:whatsapp) — пользователь выбирает из перечисленных. Без параметра доступны все включённые каналы.

10. Контрольная точка

Поднимите оба процесса на адресах, которых ждёт конфигурация: issuer — на порту из Authority, клиент — на порту своего redirect_uri:
Годится и прописать те же адреса в applicationUrl файла Properties/launchSettings.json каждого проекта. Клиент сам ходит к issuer’у по https, поэтому dev-сертификат ASP.NET Core должен существовать и быть доверенным: создайте его командой dotnet dev-certs https, затем доверьте командой dotnet dev-certs https --trust. На Linux команда доверия тоже работает и печатает значение SSL_CERT_DIR, которое нужно выставить; строка There was an error trusting the HTTPS developer certificate., которой она заканчивается, там ожидаема и провала шага не означает — экспортируйте напечатанное значение, оставив в списке каталог сертификатов вашего дистрибутива, иначе перестанут проверяться чужие цепочки (restore с nuget.org, любые исходящие HTTPS-вызовы):
Второй элемент — каталог сертификатов вашего дистрибутива, и команда вычисляет его, а не зашивает: openssl version -d печатает OPENSSLDIR — каталог конфигурации OpenSSL, а не сами сертификаты, — а файлы CA с hash-ссылками, по которым OpenSSL и ищет, лежат в его подкаталоге certs, отсюда /certs на конце. На Debian и Ubuntu получается /usr/lib/ssl/certs, на других дистрибутивах префикс другой — поэтому он читается, а не выписывается. После этого сертификат доверен. Подробности — в разделе «Trust HTTPS certificate on Linux» статьи Enforce HTTPS in ASP.NET Core.
1

Запустите приложение

Поднимите оба процесса, как показано выше, и откройте клиента по адресу https://localhost:7020.
2

Запустите поток

Выберите «Войти через Veriqa» и отсканируйте QR доверенным каналом на телефоне. У Email QR есть в режиме Push; в режиме Pull вместо него — поле адреса и кнопка «Get a link».
3

Подтвердите на телефоне

Подтвердите запрос в MAX / Telegram / WhatsApp / другие мессенджеры / Email. Десктоп обновит статус по SignalR или polling.
4

Подтвердите на десктопе

У Email — и у любого канала при Veriqa:LoginConfirmation:Mode = OnWebPage — страница входа затем спрашивает ещё раз: подтвердите вход там.
5

Проверьте результат

Убедитесь, что вы вошли и ожидаемые claims на месте.

Если Veriqa работает отдельно

Всё выше — встроенный режим: Veriqa живёт пакетами внутри вашего хоста. Тот же сервер может работать и отдельно, а приложение входит в него по обычному OIDC — облако Veriqa, ваш контейнер Docker или служба (systemd либо служба Windows) на вашей машине. Для .NET-приложения это меняет дерево зависимостей, а не код: остаётся клиентская половина шага 5, а все пакеты Veriqa уходят — ни один компонент Veriqa не попадает в вашу сборку и в SBOM.
Veriqa Cloud сейчас недоступен — строка про облако ниже описывает контракт его API. Используйте self-hosted; подробности — в Veriqa Cloud.
Обвязка — та же, что в шаге 5, с одним отличием: адрес issuer’а берётся из конфигурации, а не из кода, потому что это единственное значение, которое меняется между режимами и между окружениями.
Program.cs (клиентское приложение)
appsettings.json (клиентское приложение)
Confidential-клиент добавляет options.ClientSecret — из user-secrets в разработке и из секрет-стора в продакшне, но не из appsettings.json. В облаке секрет выдаётся отдельным явным действием на экране Applications и показывается один раз. Больше ничто на этой странице от режима не зависит: эндпоинты шага 7, claims шага 8 и ограничение канала через acr_values (шаг 9) — это поведение issuer’а, и клиент видит его через discovery, в каком бы режиме тот ни работал. Как именно поставлен отдельный сервер — следующий выбор, и на клиентскую сторону он не влияет: контейнер Docker — быстрый старт self-hosted — либо архив, который ставится юнитом systemd или службой Windows, без Docker и без рантайма .NET на машине — установка службой ОС.

Дальше

Настройка и кастомизация

Дизайн страницы входа, локализация, точки расширения.

Hardening к продакшну

Что проверить перед выпуском в продакшн.

OIDC + Veriqa: разбор

Где Veriqa встаёт в стандартный OIDC-поток.

Архитектура

Строительные блоки коннектора.

Veriqa отдельным сервером

Тот же issuer вне вашей сборки — в Docker или службой ОС.