# Подтверждение личности вошедших пользователей

> Распознавайте вошедших пользователей в чат-виджете: ключ подтверждения, расчёт userHash на Node.js, PHP и Python, подтверждённая почта и выход.

Если на вашем сайте или в приложении пользователи входят в аккаунт, виджет может их распознать. Тогда человек видит свои диалоги на любом устройстве, а команда видит один контакт вместо нескольких анонимных.

## Зачем нужна подпись

Браузеру доверять нельзя: любой может вызвать `identify` с чужой эл. почтой. Поэтому ваш сервер вычисляет `userHash`, подпись HMAC-SHA256 с ключом подтверждения, который браузер никогда не видит.

## Ключ подтверждения

Ключ находится в настройках чат-виджета на вкладке «Подтверждение личности». Видеть и менять его могут только владельцы.

- Храните ключ только на сервере, например в переменной `SUPPORT_IDENTITY_SECRET`. Никогда не вставляйте его в код браузера.
- После создания нового ключа старый работает ещё 24 часа, чтобы вы успели обновить сервер.

![Настройки подтверждения личности со скрытым ключом](https://hrc.lt/screens/ru/project-identity.desktop.1600.589ff4ae.webp)

## Что подписывать

Подпись вычисляется из строки `v1:<ID>:<эл. почта>`. Эл. почта без пробелов по краям и строчными буквами; недостающая часть остаётся пустой.

```text
v1:<externalId>:<эл. почта без пробелов по краям, строчными буквами>

v1:12345:jonas@example.com   ID и эл. почта
v1:12345:                    только ID
v1::jonas@example.com        только эл. почта, нужна настройка
                             «Моё приложение проверяет адреса эл. почты»
```

`userHash` это HMAC-SHA256 этой строки с ключом подтверждения, записанный 64 шестнадцатеричными символами.

## Код для сервера

Node.js:

```js
// Node.js (Express): ваш endpoint, только для вошедшего пользователя
import { createHmac } from 'node:crypto';

app.get('/api/support-hash', requireLogin, (req, res) => {
  const user = req.user;
  const email = (user.email ?? '').trim().toLowerCase();
  const canonical = `v1:${user.id ?? ''}:${email}`;
  const userHash = createHmac('sha256', process.env.SUPPORT_IDENTITY_SECRET)
    .update(canonical)
    .digest('hex');
  res.json({ userHash });
});
```

PHP:

```php
<?php
// PHP: mb_strtolower, не strtolower, чтобы Ж и Ё тоже стали строчными
$email = mb_strtolower(trim($user->email ?? ''), 'UTF-8');
$canonical = 'v1:' . ($user->id ?? '') . ':' . $email;
$secret = getenv('SUPPORT_IDENTITY_SECRET');
$userHash = hash_hmac('sha256', $canonical, $secret);
```

Python:

```python
# Python
import hashlib
import hmac
import os

user_id = "" if user.id is None else str(user.id)
email = (user.email or "").strip().lower()
canonical = f"v1:{user_id}:{email}"
secret = os.environ["SUPPORT_IDENTITY_SECRET"].encode()
user_hash = hmac.new(secret, canonical.encode(), hashlib.sha256).hexdigest()
```

## Код для браузера

Всегда передавайте `externalId` строкой: число игнорируется.

```js
// В браузере: получите подпись пользователя от своего сервера
const { userHash } = await fetch('/api/support-hash', {
  credentials: 'include',
}).then((r) => r.json());
HrcWidget('identify', {
  externalId: String(user.id ?? ''),
  email: user.email,
  name: user.name,
  userHash,
});

// При выходе
HrcWidget('reset');
```

С `identify` можно передать и поля профиля, смотрите [Профиль посетителя](https://hrc.lt/ru/docs/widget/visitor-profile).

## «Моё приложение проверяет адреса эл. почты»

Включайте, только если ваше приложение проверяет, что эл. почта принадлежит пользователю (отправляет код или ссылку). Тогда подписанная эл. почта объединяет письма и диалоги одного человека в один контакт во всех чат-виджетах и почтовых ящиках.

Пока эта настройка выключена, всегда передавайте `externalId` пользователя. Подписанная эл. почта без ID отклоняется с ошибкой `IDENTITY_INVALID`, а эл. почта считается не подтверждённой, пока человек не ответит на наше письмо.

## Только для вошедших

Режим «Только для вошедших» разрешает писать только распознанным пользователям. Анонимная сессия не может отправлять сообщения и менять профиль (`IDENTITY_REQUIRED`).

## Проверьте подпись

В настройках есть поле проверки вычисленного значения: введите ID, эл. почту и значение, вычисленное на сервере, и приложение скажет, подходит ли оно. Проверка выполняется на сервере, ключ в браузер не отправляется. Если не подходит, проверьте, что эл. почта в нижнем регистре и что вы используете ключ этого виджета.

Ошибку вы видите и на своём сайте: `HrcWidget('on', 'identify-failed', ...)` получает код, но никогда не саму подпись.

## Выход

Когда пользователь выходит, вызовите `HrcWidget('reset')`. Виджет забудет сессию, и следующий человек на том же устройстве не увидит чужих диалогов.
