LEX — AI Legal Platform for Law Firms

AI-powered legal analysis platform for law firms and corporate counsel.

Features

Resources

Blog Articles

Technology

Built on AWS (EC2, Bedrock Claude AI, ALB, WAF, S3, ACM, KMS). PostgreSQL, Redis, Qdrant vector database. TypeScript, React, Node.js.

Start free — 50 credits on registration. Sign up

TECH 8 хв

Дія.Підпис для бізнесу: технічні виклики інтеграції з державним сервісом

ECDSA + SHA256 для хешування. Redis key mismatch між start та verify. QR-код і deep link. Оновлення даних ФОП/ТОВ при кожному логіні. 4 фікси за добу. Реальна історія інтеграції з Дією — без прикрас.

Дія.Підпис для бізнесу: технічні виклики інтеграції з державним сервісом

Реальна історія: як ми інтегрували Дію.Підпис і що пішло не так (і як ми це полагодили).


Навіщо Дія.Підпис

Google OAuth — зручний, але не юридично значущий. Для LegalTech-платформи це проблема: ми маємо знати, що користувач — це конкретна фізична особа або ФОП/ТОВ, а не просто власник Gmail-акаунту.

Дія.Підпис (Diia.Sign) вирішує це:

Підключення до Дії — обов'язковий крок для будь-якої платформи, яка працює з юридичними документами в Україні.


Архітектура: як це працює

Флоу автентифікації

Користувач → LEX (натискає "Увійти через Дію")
     ↓
LEX Backend → Diia API: POST /api/v2/auth/acquirer/branch/offer/request
     ↓
Diia API → LEX: { deeplink, requestId }
     ↓
LEX Frontend → показує QR-код (з deeplink) або редіректить на deep link
     ↓
Користувач → сканує QR у Дії, підтверджує
     ↓
Diia API → LEX Callback: POST /api/diia/callback
     ↓
LEX Backend → розшифровує дані, створює/оновлює користувача
     ↓
LEX Backend → видає JWT, редіректить на фронтенд

Ключові компоненти

Компонент Технологія
Хешування requestId ECDSA + SHA256, Base64
Зберігання стану Redis (TTL 5 хвилин)
Розшифрування даних AES-256-CBC (ключ від Дії)
Сесія JWT з даними користувача

Проблема 1: ECDSA хешування requestId

Що було

Дія вимагає, щоб requestId був підписаний ECDSA з SHA-256 і закодований у Base64. Документація мінімальна. Наша перша реалізація:

// ❌ Не працювало
const hash = crypto.createHash('sha256')
  .update(requestId)
  .digest('hex');

Що пішло не так

Дія очікувала не просто хеш, а підпис ECDSA з приватним ключем acquirerToken. Формат підпису — DER, закодований у Base64. Hex-хеш — це зовсім інше.

Як полагодили

// ✅ Правильно
const sign = crypto.createSign('SHA256');
sign.update(requestId);
const signature = sign.sign(privateKey, 'base64');

Ключовий момент: приватний ключ — це не acquirerToken напряму, а PEM-ключ, який генерується при реєстрації в порталі Дії.


Проблема 2: Redis key mismatch

Що було

Флоу: /start створює запит і зберігає requestId в Redis. Коли callback приходить від Дії — бекенд шукає цей requestId в Redis, щоб зіставити з сесією.

// /start endpoint
await redis.set(`diia:request:${requestId}`, sessionData, 'EX', 300);

// /callback endpoint
const session = await redis.get(`diia:auth:${requestId}`);
// 💥 null — ключі не збігаються!

Що пішло не так

Два різних префікси: diia:request: при створенні, diia:auth: при верифікації. Класичний copy-paste баг. Callback приходив, але Redis повертав null, і автентифікація мовчки фейлилась.

Як полагодили

Уніфікували префікс:

const REDIS_PREFIX = 'diia:auth:';

// /start
await redis.set(`${REDIS_PREFIX}${requestId}`, sessionData, 'EX', 300);

// /callback
const session = await redis.get(`${REDIS_PREFIX}${requestId}`);

Проблема 3: Оновлення даних бізнесу

Що було

При першому логіні через Дію ми створювали запис ФОП/ТОВ:

Але ці дані можуть змінюватись: компанія переїхала, змінила назву, оновила контактний email.

Що пішло не так

Другий і подальші логіни ігнорували нові дані від Дії — ми просто знаходили існуючий запис по ЄДРПОУ і пропускали оновлення. Результат: застарілі адреси, старі назви офертів.

Як полагодили — 4 PR за добу

PR #1117 — оновлення назв бранчів та офертів:

// Раніше: створювали тільки якщо не існує
// Тепер: UPDATE при кожному логіні
await db.query(`
  UPDATE diia_branches
  SET name = $2, address = $3, updated_at = NOW()
  WHERE acquirer_id = $1
`, [acquirerId, branchName, address]);

PR #1118 — оновлення існуючих бранчів при ініціалізації: Додали ON CONFLICT DO UPDATE для ідемпотентності.

PR #1119 — оновлення назв офертів: Назви офертів теж могли змінюватись (наприклад, "Авторизація" → "Вхід через Дію").

PR #1120 — оновлення назви компанії та email:

await db.query(`
  UPDATE organizations
  SET name = $2, email = $3, updated_at = NOW()
  WHERE edrpou = $1
`, [edrpou, companyName, contactEmail]);

Проблема 4: Nginx proto override

Що було

Дія відправляє callback на наш URL. У продакшні — за Cloudflare та Nginx. Nginx проксює запит на бекенд, але губить оригінальний протокол.

Що пішло не так

Бекенд генерував redirect URL з http:// замість https://:

# Дія callback → Nginx → Backend
# Backend бачив: req.protocol = 'http'
# Генерував: http://legal.org.ua/auth/callback
# Браузер: mixed content error

Як полагодили

Nginx конфіг:

proxy_set_header X-Forwarded-Proto $scheme;

Express middleware:

app.set('trust proxy', 1);
// Тепер req.protocol читає X-Forwarded-Proto

Поточний стан інтеграції

Параметр Значення
Тип підпису Дія.Підпис (КЕП)
Хешування ECDSA + SHA-256 + Base64
Стан сесії Redis, TTL 5 хв
Шифрування AES-256-CBC
JWT RS256, 7 днів
Оновлення даних При кожному логіні

Що отримуємо від Дії

При успішній автентифікації Дія повертає:

Ці дані автоматично синхронізуються з нашою базою при кожному логіні.


Уроки

  1. Документація Дії — мінімальна. Готуйтесь до reverse engineering. Тестове середовище працює інакше, ніж продакшн.

  2. Redis-ключі мають бути константами. Один prefix, один файл з константами. Ніякого дублювання рядків.

  3. Дані потрібно оновлювати при кожному логіні. Не тільки створювати, а й синхронізувати. Бізнес-дані змінюються.

  4. Тестуйте весь флоу end-to-end. Unit-тести не покривають: "callback приходить, але Redis ключ інший". Тільки повний прогін від /start до JWT видає баг.

  5. Nginx — невидимий убивця. X-Forwarded-Proto, X-Real-IP, trust proxy — конфігуруйте до того, як інтеграція піде в продакшн.


Дія.Підпис — це правильний вибір для юридичної платформи. Але шлях від "документація виглядає просто" до "все працює в проді" — це 4 PR за добу і купа нетривіальних багів.


Реєстрація: legal.org.ua