# SERVICE-GUIDE.md — вход по Niko Trust в вашем сервисе Рецепт для владельца сервиса: как принимать Niko Trust как основной способ аутентификации. Полный рабочий код — [`examples/service`](../examples/service) (сервис) и [`examples/approve`](../examples/approve) (роль кошелька). ## Модель в двух абзацах Идентичность — это пара ключей Ed25519; адрес `trust1…` выведен из публичного ключа (bech32). Сервис не видит паролей: он публикует **ApprovalRequest** — подписанный вопрос «войти?» адресованный конкретному адресу, действительный ≤60 секунд. Владелец кошелька (приложение niko_trust_gui / Android) отвечает **ApprovalResponse**, который криптографически привязан к байтам именно этого запроса (INV-4). Релей — тупое хранилище конвертов: он не проверяет подписи и не знает, кто прав; всю верификацию сервис делает сам локально. Что это даёт по безопасности: фишинг невозможен (запрос подписан ключом сервиса и показывается в кошельке с его адресом), повтор невозможен (ответ умирает вместе с окном запроса), подмена решения невозможна (ответ подписан ключом пользователя и привязан к хешу запроса), а компрометация релея не даёт атакующему ничего, кроме отказа в обслуживании. Есть два способа подтверждения: - **Адресный** (ниже): сервис уже знает `trust1...` пользователя и шлёт запрос на его кошелёк. - **QR / reverse** (без знания адреса): пользователь сканирует QR телефона и сам отправляет подтверждение сервису. Подходит и для входа, и для регистрации. Формат и правила — в [QR-LOGIN.md](QR-LOGIN.md). ## Поток логина ``` браузер ваш сервис релей кошелёк | GET /login?user=X | | | |----------------------->| ApprovalRequest(action= | | | | "login", recipient=X) | | | |-- POST /v1/objects ------->| | |<- { id } --------------| |--- push ----->| | | GET /v1/responses?request=id (или WS) | | ...пользователь жмёт «Разрешить» в кошельке... |<-- store -----| | GET /login/status |<-- envelope {tce,sig} -----| | |----------------------->| VerifyApprovalResponse() | | |<- {approved, user:X} --| → своя сессия | | ``` ## Шаги ### 1. Идентичность сервиса Сгенерируйте один раз ключ и храните сид как секрет (это «сертификат» сервиса): ```go sv, _ := signer.Generate() fmt.Println(sv.Address(), hex.EncodeToString(sv.Seed())) // при старте: signer.FromSeed(seedBytes) ``` Адрес сервиса увидит пользователь в кошельке рядом с текстом запроса. ### 2. Создание запроса на вход ```go req := &protocol.ApprovalRequest{ Sender: sv.Public(), Recipient: []byte(userAddr.PubKey()), // чей это вход Action: "login", Payload: map[string]tce.Value{"session": tce.String(sessionID)}, Message: "Sign in to demo service", // человек это прочитает CreatedAt: uint64(time.Now().Unix()), ExpiresAt: uint64(time.Now().Unix() + 60), Nonce: nonce16(), } tceBytes, _ := protocol.EncodeApprovalRequest(req) sig := sv.Sign(tceBytes) id := tce.ComputeID(tceBytes) // = object id, им же ссылается ответ POST {base}/v1/objects {"tce": b64(tceBytes), "signature": b64(sig)} ``` `Recipient` обязан быть валидным адресом: запрос всегда адресный. ### 3. Ожидание ответа Два способа: - **Опрос** (просто): `GET /v1/responses?request=` каждые ~0.7 c. Требует сессию чтения — см. шаг 5. - **WebSocket** (правильно): подключиться к `GET /v1/ws` со своим токеном и отправить `{"op":"subscribe","channel":"responses","key":""}` — события приходят мгновенно (docs/API.md §WebSocket). ### 4. Верификация — единственная критичная строка ```go resp, err := protocol.VerifyApprovalResponse(reqTCE, reqSig, respTCE, respSig) if err == nil && resp.Decision == protocol.Allow && bytes.Equal(resp.Responder, []byte(userAddr.PubKey())) { // адрес верифицирован → выпустить свою сессию } ``` Проверка уже включает: строгий декод обоих объектов, подпись под ключом ответившего, привязку к точным байтам вашего запроса и окно времени. Сверка `Responder` с ожидаемым адресом отсекает ответы чужих кошельков. ### 5. Сессия чтения релея Чтение лент требует токена: `POST /v1/auth/challenge` → подписать `AuthAssertion` (audience взять из `GET /v1/config`) → `POST /v1/auth/assert` → `session_token`, живёт 30 минут (docs/API.md). Готовые реализации: `pkg/protocol.VerifyAuthAssertion`-клиенты в примерах выше. ## Где брать готовый клиентский код | Язык | Пакет | |-----------|-----------------------------------------------------------------------| | Go | этот модуль: `pkg/tce`, `pkg/protocol`, `pkg/verify`, `pkg/address`, `pkg/identity/signer` | | Rust | крейт `niko_trust_gui` (git-зависимость), модули `tce`, `protocol`, `relay`; пример `examples/service_login.rs` | | Kotlin/JVM| модуль `:sdk` репозитория niko_trust_android (`RelayClient`, `LiveFeed`, протокол) | ## Эксплуатация - Релей за обратным прокси? Включите `trust_proxy: true` в config.yaml, иначе рейт-лимиты считают всех одним IP. - Рестарт релея сбрасывает сессии — клиенты перекладываются сами; ваш сервис должен переживать 502/401 во время окна рестарта. - Храните сид сервиса как секрет: он подписывает все запросы, и пользователь видит его адрес в каждом подтверждении.