> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Быстрый старт JavaScript для аутентификации Didit

> Подключите приложение аутентификации Didit к странице входа с PKCE: проверка URL обратного вызова, сессий и выход из системы.

> [!WARNING]
>
> Аутентификация является новой функцией: используйте этот пример входа через размещённую страницу в приложении, для которого включена аутентификация. Пакет `@didit-protocol/auth` ещё не опубликован, а API пакета ниже является предварительной версией, а не устанавливаемым примером быстрого старта.

## Настройка входа через размещённую страницу

Зарегистрируйте точный URL обратного вызова (callback URL) и origin веб-приложения для вашего приложения в песочнице. Скопируйте его origin для входа (`https://<slug>.sandbox.auth.didit.me`) из консоли, как описано в [обзоре](https://docs.didit.me/ru/authentication/overview.md). В этом примере используются криптографические API браузера, которым требуется HTTPS или localhost в песочнице: что именно регистрировать, см. в разделе [локальная разработка](https://docs.didit.me/ru/authentication/overview.md#локальная-разработка).

Приложение отвечает за `state` и PKCE-верификатор. Вход через размещённую страницу отвечает за экраны пароля, email, passkey, телефона, SSO и обязательного MFA. В этом коде не должно быть API-ключа.

```javascript auth.js theme={null}
// Replace both values with your registered application configuration.
const AUTH_BASE = "https://YOUR_SLUG.sandbox.auth.didit.me/";
const REDIRECT_URI = "https://YOUR_APP_ORIGIN/auth/callback";
const PENDING = "didit.auth.pending";
let tokens = null;

function base64url(bytes) {
  return btoa(String.fromCharCode(...bytes))
    .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

export async function signIn() {
  const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
  const state = base64url(crypto.getRandomValues(new Uint8Array(32)));
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
  sessionStorage.setItem(PENDING, JSON.stringify({ verifier, state, startedAt: Date.now() }));
  const url = new URL("sign-in", AUTH_BASE);
  url.search = new URLSearchParams({
    redirect_uri: REDIRECT_URI,
    state,
    code_challenge: base64url(new Uint8Array(digest)),
    code_challenge_method: "S256",
  }).toString();
  window.location.assign(url.href);
}

export async function handleCallback() {
  const query = new URLSearchParams(window.location.search);
  const saved = sessionStorage.getItem(PENDING);
  sessionStorage.removeItem(PENDING);
  history.replaceState(null, "", window.location.pathname);
  if (!saved) throw new Error("Start sign-in again.");
  const pending = JSON.parse(saved);
  if (query.get("state") !== pending.state || Date.now() - pending.startedAt > 600000) {
    throw new Error("Invalid or expired sign-in. Start again.");
  }
  if (query.has("error") || !query.get("code")) throw new Error("Sign-in was not completed.");
  const response = await fetch(new URL("token/", AUTH_BASE), {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "authorization_code",
      code: query.get("code"),
      code_verifier: pending.verifier,
      redirect_uri: REDIRECT_URI,
    }),
  });
  if (!response.ok) throw new Error("Code exchange failed. Start sign-in again.");
  tokens = await response.json();
  return getSession();
}

export async function getSession() {
  if (!tokens) return null;
  const response = await fetch(new URL("session/", AUTH_BASE), {
    headers: { Authorization: `Bearer ${tokens.access_token}` },
    cache: "no-store",
  });
  if (!response.ok) {
    tokens = null;
    throw new Error("Session check failed. Sign in again.");
  }
  return response.json();
}

export async function signOut() {
  const current = tokens;
  tokens = null;
  if (!current) return;
  const response = await fetch(new URL("session/", AUTH_BASE), {
    method: "DELETE",
    headers: { Authorization: `Bearer ${current.access_token}` },
  });
  if (response.status !== 204) throw new Error("Local state cleared; server logout failed.");
}
```

Вызовите `signIn()` из кнопки входа. На зарегистрированной странице обратного вызова один раз вызовите `handleCallback()`, отобразите состояние загрузки и покажите ошибку, если вызов завершился неудачей. Для текущего пользователя используйте `getSession()`, а для выхода из системы `signOut()`. Страница обратного вызова удаляет свой код из адресной строки до отправки запроса.

Этот минимальный пример хранит токены в памяти и снова выполняет вход после перезагрузки страницы или истечения срока действия токена доступа. Для постоянных сессий используйте свой бэкенд с защищёнными cookie или реализуйте сериализованную [ротацию refresh-токенов](https://docs.didit.me/ru/authentication/tokens-and-jwks.md). Никогда не помещайте refresh-токены в URL или логи. Проверка сессии на фронтенде влияет только на отображение; ваш бэкенд должен проверять вызывающего пользователя при каждом защищённом запросе.

## Предварительная версия SDK аутентификации

Планируемое ядро `@didit-protocol/auth` будет предоставлять `createDiditAuth({publishableKey})`, `signIn()`, `handleRedirectCallback()`, `getSession()`, `getToken()`, `onAuthStateChange` и `signOut()`, а также `vendorData` в объекте идентификации. Предполагается, что оно добавит автоматические сигналы устройства и серверную точку входа с `verifyToken()`.

Публикуемый ключ (publishable key) имеет вид `pk_live_` (для рабочего приложения) или `pk_test_` (для приложения в песочнице), за которым следует base64url-кодирование origin вашего приложения без символов заполнения. SDK отправляет свои API-вызовы на этот origin. Ключ является публичным: он идентифицирует приложение и не является секретом.

### Step-up и повторная аутентификация в предварительной версии

* `stepUp({ action, amount?, currency?, redirectUri? })` вызывает `POST step-up/` вашего приложения, чтобы узнать, требует ли `action` проверки личности. `action` может быть `payout`, `bank` или `contact`, а для выплаты нужен `amount`. Если применяется одно из ваших правил step-up, пользователь перенаправляется на страницу верификации, которую вернул Didit.
* `handleStepUpCallback(url?)` выполняется на странице, на которую возвращается пользователь. Он читает `?step_up=<id>`, удаляет его из адресной строки и получает результат через `GET step-up/{id}/`.
* `reauthenticate({ method? })` повторно выполняет вход того же пользователя на размещённой странице (`method: "redirect"`, по умолчанию) или с помощью passkey (`method: "passkey"`, только на домене входа Didit вашего приложения). После размещённой страницы завершите вход через `handleRedirectCallback()`. Если вход завершит другой человек, метод завершится ошибкой `step_up_user_mismatch`, а исходная сессия сохранится.

Оба метода step-up возвращают `{ id, status, approved, rule, method }`, где `status` может быть `not_required`, `pending`, `approved`, `review`, `declined` или `expired`. `redirectUri` по умолчанию равен адресу перенаправления клиента и должен точно совпадать с одним из зарегистрированных адресов перенаправления вашего приложения: если требуется проверка, Didit отклоняет любой другой адрес. Прежде чем ваш бэкенд применит результат step-up, повторно запросите результат через `GET step-up/{id}/` с токеном доступа пользователя, поскольку результат, переданный браузером, может быть изменён.

Headless-вход также может вернуть `protection_required`, если правила защиты вашего приложения его пометили. Тогда пользователь завершает вход кодом, который Didit отправил ему по электронной почте (`protection.verify({ code })`), или с помощью passkey (`protection.passkey()`). Поле `methods` перечисляет, какими из этих двух способов он может воспользоваться.

Не устанавливайте предполагаемую версию и не пишите код по предполагаемым параметрам обратного вызова. Прежде чем заменять пример с размещённой страницей, подтвердите экспорты опубликованного пакета, настройки издателя и конфигурацию ключей. Пароль, код из электронного письма, магическая ссылка, passkey, телефон, MFA, вход через социальные сети, step-up и методы агентов будут зависеть от API, поддерживаемого в этом релизе.

## Добавление других продуктов

Ваш бэкенд проверяет токен доступа (см. [токены и JWKS](https://docs.didit.me/ru/authentication/tokens-and-jwks.md)) и читает его `vendor_data`. Используйте это значение как `vendor_data` при [создании сессии верификации](https://docs.didit.me/ru/sessions-api/create-session.md) или [выпуске токена транзакции](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md). Успешный вход не означает, что какой-либо из этих продуктов одобрил пользователя.

Перед запуском проверьте на включённом приложении в песочнице регистрацию, повторный вход, отмену, несовпадение state, повторное использование кода, восстановление пароля, MFA, истечение срока действия и выход из системы.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.