Привет, Хабр! На связи разработчик Куратов Кирилл из команды дирекции качества РТЛабс. Представлю вам нашу внутреннюю разработку — User Pool.

По названию понятно, что это пул пользователей, но глобально он представляет собой сервис для получения данных тестовых учётных записей. Наша команда использует User Pool во фреймворке для написания автотестов и в браузерном расширении для автоматической авторизации тестовых учётных записей в тестируемых сервисах. Например, в единой системе идентификации и аутентификации (ЕСИА), речь о которой пойдёт ниже. Также в конце будет небольшая история о том, как мы разрабатывали расширение для браузера и с какими трудностями столкнулись.

ЕСИА: сложный процесс авторизации с уникальными требованиями

Что такое ЕСИА и зачем мы к ней обращаемся
Госуслуги — один из основных государственных сервисов и должен быть надёжным. Для его тестирования существуют разные стенды, на которых обкатываются услуги команд. Это всё также надо тестировать.

ЕСИА — единая система для авторизации на Госуслугах и других государственных сервисах, посредством генерации токена. В нашем случае мы хотим получить этот токен и использовать его для внедрения в автотест во фреймворке или в расширении для браузера. ЕСИА — это не о тестах, а о единой системе идентификации, аутентификации и авторизации.

При работе с ЕСИА была интересная проблема, с которой можно столкнуться почти на любом сайте. Это защита от DDOS. При запуске тестов в ЕСИА будут одновременно идти пара десятков запросов, что логично заставит ЕСИА заблокировать на время доступ по айпи и не просто заблокировать, а заставит проходить капчу. Выбрались из этой ситуации довольно просто — внесли айпишники на которых работает User Pool в white-листы, но, к сожалению, локально запустить тесты в параллели не получится.

Также с ЕСИА была другая интересная ситуация. Если вы попробуете зайти в свою учётную запись (УЗ), вероятнее всего, придётся вводить код с телефона, и тестовые УЗ от этого не защищены. Как мы поступили в этом случае? Внесли в white-лист УЗ и добавили метод, чтобы при такой аномалии на них приходил одинаковый код. На этом все проблемы с ЕСИА закончились.

Отдельно хочу отметить, что в User Pool включена авторизация по ЕСИА. В статье она постоянно будет встречаться, ниже напишу про неё — почему для нас она так важна.

Проблемы с ЕСИА

  1. Сложный процесс авторизации. Для получения токена в ЕСИА необходимо выполнить три шага: запросить авторизацию, подтвердить через смс или email и после этого получить токен. Из-за этого авторизация становится трудоёмким и сложным для автоматизации процессом. Каждый стенд ЕСИА, то есть тестовое окружение, имеет свои особенности, что требует адаптировать процесс авторизации под каждое.

  1. Разные роли и параметры УЗ. Тестовые учётные записи в ЕСИА имеют разные роли — ИП, организация, физическое лицо, и параметры, например регион или сервис. Это требует гибкого подхода к управлению УЗ, чтобы каждая команда могла запрашивать учётные записи с нужными параметрами, и токен приходил с определённого стенда.

Как User Pool решает проблемы с ЕСИА

  1. Автоматизация процесса авторизации:

  • автоматизирует сложный процесс в ЕСИА, выполняя все три шага за тестировщика. Это позволяет быстро получать токен и использовать его в тестах,

  • учитывает особенности разных стендов ЕСИА, что делает процесс авторизации универсальным для всех окружений.

2. Обработка защиты от DDOS и капчи:

  • User Pool интегрирован с белыми списками IP-адресов, что позволяет избежать блокировок со стороны ЕСИА при параллельном выполнении тестов,

  • когда ЕСИА требует прохождения капчи, User Pool использует заранее подготовленные коды подтверждения, что упрощает процесс авторизации.

3. Гибкость для разных ролей и параметров УЗ:

  • предоставляет гибкий API, который позволяет каждой команде запрашивать УЗ с нужными параметрами. Например, можно запросить УЗ с определённой ролью — ИП, организация, физическое лицо, или для конкретного сервиса,

  • каждая УЗ имеет набор параметров — дополнительных данных, которые учитываются при поиске. Это позволяет легко находить нужные УЗ для разных сценариев тестирования.

Для удобства и простоты понимания объединим эти данные в таблицу:

Проблема

Решение с User Pool

Разрозненные механизмы управления УЗ

Централизованное управление через API

Дублирование кода при работе с учётными данными в разных сервисах

Единая точка доступа, устраняющая необходимость дублирования

Необходимость синхронизировать изменения в разных частях системы

Единый сервис, упрощающий обновление логики работы с УЗ

Проблемы безопасности при хранении учётных данных на локальных машинах

Централизованное безопасное хранилище — Vault

Ограниченные возможности параллельного выполнения тестов

Механизм блокировки и освобождения УЗ

Почему существующие фреймворки не справились с ЕСИА

Они не предоставляют встроенных механизмов для работы с такими сложными процессами авторизации, как в ЕСИА. Для интеграции пришлось бы разрабатывать кастомные плагины и сценарии, что значительно увеличило бы сложность поддержки. Большинство из них не поддерживает автоматическую обработку капчи.

Итог: свой велосипед лучше чужого грузовика

Иногда проще построить свою лёгкую и удобную модель, чем пытаться превратить многотонный грузовик в простой горный велосипед. User Pool — это именно тот случай. Мы выбрали нелёгкий путь, но в итоге получили решение, которое идеально подходит для наших задач и не требует постоянных костылей для адаптации чужого инструмента.

Может возникнуть вопрос: почему не использовать стандартные базы данных и существующие решения? Ответ прост: стандартные инструменты не обеспечивали нужный уровень гибкости и удобства.

Отдаём предпочтение нашему сервису за преимущества
Отдаём предпочтение нашему сервису за преимущества

Почему выбрали свой путь: User Pool против Amazon Cognito, Keycloak и ORY Hydra & Kratos

Мы рассмотрели несколько популярных альтернатив — Amazon Cognito, Keycloak и ORY Hydra & Kratos — и пришли к выводу, что ни одно из них не подходит для наших задач. Почему?

Общие признаки у всех фреймворков

  • ЕСИА — нетиповой флоу. Готовые IdP не «исполняют» многошаговый браузерный сценарий ЕСИА — редиректы, 2FA/SMS, промо-MFA, капча, особенности стендов, и не выдают или не проставляют cookie acc_t на *.gosuslugi.ru. Это требует отдельного клиента или скриптов и интеграции на уровне браузера.

  • Нет модели аренды тестовых УЗ. Отсутствует нативная семантика lock/TTL/release для конкурентных прогонов. Нужен внешний механизм, например Redis, и свой API оркестрации.

  • Нет доменной аллокации аккаунтов. Сложная выборка по атрибутам — роль ФЛ/ЮЛ/ИП, сервис, environment, регион, история использования, и правила бронирования — это бизнес-логика, которую всё равно надо реализовывать в отдельном сервисе.

  • Инфра и операционная стоимость. Перенос авторизации в чужую платформу или стек ломает текущие предпосылки — whitelist IP к ЕСИА, Vault, PostgreSQL, Redis, добавляет квоты или лимиты и рост TCO.

  • Избыточность. Большая часть функциональности прод-IAM нам не нужна, настройка или поддержка выше, чем у узкоспециализированного сервиса

Пробежимся конкретнее по фреймам

Amazon Cognito

Природа: Managed IdP в AWS (User Pools/Identity Pools, OIDC/OAuth2/SAML, Hosted UI, MFA, Lambda-триггеры). Выдаёт JWT (ID/Access/Refresh).

Ограничения для нас:

  • не автоматизирует ЕСИА-флоу и не проставляет acc_t на госдомен,

  • нет встроенного lock/TTL/release для тестовых УЗ — потребуются Lambda + внешнее хранилище,

  • доменная аллокация — роль/сервис/env/регион/история, придётся писать свой слой,

  • привязка к AWS — сети/квоты/стоимость, конфликтует с текущей инфраструктурой — PostgreSQL, Vault, Redis, whitelist к ЕСИА.

ORY Hydra & Kratos

Природа: Hydra — Authorization Server — OAuth2/OIDC, без хранения пользователей, Kratos — управление пользователями и self-service флоу. Часто вместе с Oathkeeper/Keto. Хранилище — обычно PostgreSQL.

Ограничения для нас:

  • многосервисная композиция: нужно стыковать несколько компонентов, описывать флоу — высокий входной порог/поддержка,

  • нет встроенного lock/TTL/release — снова внешний сервис оркестрации,

  • ЕСИА и acc_t не закрываются «из коробки» — потребуется отдельный клиент или расширение и ваша оркестрация.

Как User Pool может пригодиться в вашем проекте

Допустим, у вас есть несколько микросервисов, которым нужны учётные данные пользователей, например для логина. Кейс: в нашем приложении для тестировщиков есть функция автологина. Как это реализовать? Мы связываем наше приложение для автологина с User Pool, API которого предоставляет четыре базовые операции: Create, Delete, Update, Get.

Для автологина можно использовать методы: /api/v1/users/authUser — возвращает куку авторизации любого пользователя по параметрам.

Идеальное решение: API абстрагирует вас от подкапотных процессов, позволяя быть уверенными, что мы будем получать данные для автологина.

Стек технологий

User Pool реализован с использованием следующих технологий:

Java + Spring — основной язык и фреймворк, поскольку автотесты в проектах пишутся на Java,

PostgreSQL — основная база данных для хранения учётных записей и их атрибутов,

Vault — хранилище паролей и генерация токенов для безопасного доступа,

Feign Client (Apache Client) — удобный HTTP-клиент для работы с API,

OpenAPI Swagger — инструмент для документирования API.

Вот как примерно выглядит всё, что используется в User Pool:

Технологии, которые используются в User Pool
Технологии, которые используются в User Pool

Теперь можно рассмотреть весь процесс получения данных учётной записи. Для простоты понимания уйду от технической стороны и приведу упрощённое описание всех процессов.

Так как в нашей команде было принято решение сделать максимально защищённую систему авторизации и не возвращать пароли, в ответе используется кука. Во время запуска теста первым действием происходит запрос на авторизацию в сервисе ЕСИА и получение самой куки. Внутренний метод фреймворка посылает запрос на адрес пула /userpool/api/v1/users/auth с телом запроса:

Запрос на адрес пула
Запрос на адрес пула

ответ на этот запрос:

Ответ на запрос
Ответ на запрос

Что же происходит под капотом запроса /auth? Тело запроса разбивается на составляющие, которые определят, какая УЗ будет взята.

Вот как выглядит DTO ответа с учётной записью:

DTO учетной записи
DTO учетной записи

Коротко, что за поля:

  • id — генерируемый id-номер учётной записи,

  • login — в нашем случае логин это email или СНИЛС,

  • additionalData — ключ-значение, в котором мы храним данные по УЗ для её удобного поиска,

  • service, environment — это информация для внутренних команд о принадлежности УЗ.

Эти параметры отправляются через search запрос в нашу БД, и ищется подходящая учётная запись.Теперь у нас есть ID УЗ и по нему мы берём пароль из Vault.

Реализация получения пароля через Vault простая:

Получение пароля через Vault
Получение пароля через Vault

В коде это выглядит:

@Override
protected String getUserPassword(String oid) {
String password = vaultService.getPassword(oid).getPassword();
return encodeBase64(password); 
 }

Производим авторизацию через API ЕСИА и вытаскиваем куку с токеном. Упрощённо схема получения cookie выглядит так:

Получение cookie
Получение cookie

С момента вызова метода авторизации пользователя происходит несколько действий:

  • данные для входа отправляются POST запросом на ESIA,

  • в ответ приходит ссылка для перехода, по которой мы выбираем роль, остальные параметры нужны для множества других случаев и разных департаментов, для классических госуслуг это не используется,

  • по итогу из всех cookies вытаскиваем cookie с названием acc_t.

Весь код авторизации в ЕСИА очень большой, поэтому выложу укороченную версию:

public HttpCookie authorizeUser(ResponseUserDataDto userDto, Optional<Role> role, String gosuslugiUrl, String esiaUrl, String domain) {
    try {
        String password = new String(Base64.getDecoder().decode(userDto.getEncryptedPassword()));
        String login = userDto.getLogin().split(",")[0];
        AuthUserDto postLoginBody = new AuthUserDto(login, password);

        // === 1. Инициализация CookieStore для всего auth-процесса ===
        BasicCookieStore basicCookieStore = new BasicCookieStore();
        EsiaClient esiaClient = FeignHttpClient.createEsiaClient(esiaUrl, basicCookieStore);

        // === 2. Предварительное заполнение CookieStore ===
        // Добавляет стартовые cookies в зависимости от сервисов авторизации через (ESIA)
        basicCookieStoreFillerProviderService
                .fill(userDto.getService())
                .fill(gosuslugiUrl, basicCookieStore);

        // === 3. Отправка логина и пароля в ESIA ===
        ResponseEntity<ResponseDto> postLogin =
                esiaClient.postLoginAndPassword(postLoginBody);
        // === 4. Обработка post-login сценариев ===
        // - MFA (ENTER_MFA / FILL_MFA)
        // - MAX_QUIZ
        // - успешный redirect
        // - ошибки
        String redirectUrl =
                bypass2faOrPhoneConfirmation(userDto, postLogin, esiaClient);

        // === 5. Формирование итоговой авторизационной cookie ===
        HttpCookie httpCookie = httpCookieFactory
                        .get(
                                userDto.getService(),
                                userDto,
                                redirectUrl,
                                role,
                                gosuslugiUrl,
                                domain,
                                basicCookieStore
                        )
                        .get();

        log.info(String.valueOf(httpCookie));
        return httpCookie;

    } catch (FeignException ex) {
        throw new AuthMethodNotImplementedException(ex.getMessage());
    }
}

Учётная запись блокируется на то время, которое пришло в запросе или на 15 минут.

При завершении теста учётная запись освобождается запросом: /userpool/api/v1/users/release/{id}, где id нам известен и хранится в тесте.

Графически все взаимодействия User Pool можно представить как:

Схема взаимодействий User Pool
Схема взаимодействий User Pool

Как User Pool пригодился в расширении для браузера

Помимо фреймворка хочется отметить очень хорошую разработку для ручных тестировщиков — расширение для браузера для автологина.

Чтобы понимать, как работает на вид простое расширение, покажу, как оно выглядит за кулисами.

Расширение «изнутри»
Расширение «изнутри»

Суть нового расширения

Как говорилось выше, суть User Pool — это сделать процесс авторизации в аккаунт Госуслуг наиболее безопасным. Это расширение само встраивает в cookies необходимый токен для авторизации в учётную запись.

Как это происходит глазами тестировщика

После добавления расширения в браузер оно появляется в соответствующем меню.

Расширение в меню браузера
Расширение в меню браузера

Можно увидеть 5 полей с тем, что надо заполнить, и самое нижнее — Token. Это токен для доступа к User Pool.

Получение токена
Получение токена

Получить его можно через внутренний телеграм-бот. Получив токен, тестировщик выбирает необходимые параметры аккаунта для соответствия требованиям прохождений.

Выбор параметров аккаунта
Выбор параметров аккаунта

Если токен рабочий и никаких ошибок не произошло, тестировщик увидит сообщение «Пользователь авторизован» и логин будет отображаться в поле «Recent used account».

Роли УЗ
Роли УЗ

Если вы пользовались Госуслугами, то точно знаете, что существуют роли УЗ для ИП, организации, физического лица. С учётом выбора в расширении роль тоже выбирается автоматически.

Страница личного кабинета УЗ
Страница личного кабинета УЗ

От забывчивых тестировщиков, чтобы не морозить постоянно УЗ, мы сделали сессию 15 минут. По истечении времени будет появляться сообщение с выбором продления или завершения сессии.

Подкапотная часть расширения

Теперь поговорим про то, как происходила разработка этого расширения и какие были проблемы.

Начнём со стека, на котором было написано расширение: JavaScript + nodejs + typescript

На самом деле расширение по сравнению с User Pool довольно маленькое, но из-за неудобной документации компании Google доставило головной боли.

Начнём с простого, но важного файла:

{
     "name": "ЕСИА авторизация",
     "update_url": "https://clients2.google.com/service/update2/crx",
     "description": "Авторизация на госуслугах через пул учетных записей",
     "version": "__VERSION__",
     "manifest_version": 3,
     "icons": {
       "16": "resources/icons/16x16.png",
       "48": "resources/icons/48x48.png",
       "128": "resources/icons/128x128.png"
     },
     "permissions": [
       "debugger",
       "background",
       "cookies",
       "tabs",
       "webRequest",
       "storage",
       "sidePanel",
       "scripting",
       "notifications"
     ],
     "host_permissions": [
       "<all_urls>"
     ],
     "background": {
       "service_worker": "background.js",
       "type": "module"
     },
     "action": {
       "default_popup": "resources/index.html"
     }
 }

Manifest.json

Что такое manifest.json

В основе любого браузерного расширения лежит файл manifest.json. Это конфигурационный файл, который описывает, как расширение будет работать, какие разрешения ему нужны и какие ресурсы оно использует.

Что внутри manifest.json

  1. Название и описание. Расширение называется «ЕСИА авторизация», и его основная задача — автоматическая авторизация на Госуслугах через пул тестовых учётных записей.

  1. Иконки. Расширение использует три размера иконок — 16x16, 48x48, 128x128, которые отображаются в интерфейсе браузера. Это делает расширение узнаваемым и удобным для использования.

  1. Разрешения (permissions). Расширение запрашивает ряд разрешений для своей работы:

  • cookies — для работы с cookies, что необходимо для авторизации,

  • tabs — для взаимодействия с открытыми вкладками браузера,

  • webRequest — для перехвата и обработки сетевых запросов,

  • storage — для хранения данных, таких как токены и настройки,

  • scripting — для выполнения скриптов на страницах,

  • notifications — для показа уведомлений пользователю.

Эти разрешения позволяют расширению выполнять все необходимые действия для автоматической авторизации.

  1. Фоновый скрипт (background). Расширение использует service worker — фоновый скрипт, который работает в фоновом режиме и обрабатывает события, такие как авторизация или обновление токенов. Тип «module» указывает на то, что скрипт использует современный синтаксис ES6.

    • Попап (action). При нажатии на иконку расширения открывается попап — default_popup, который отображает интерфейс для выбора учётной записи и управления авторизацией. Интерфейс реализован в виде HTML-страницы (resources/index.html).

    • Надо также отметить про "manifest_version": 3. Строка говорит о том, что версия этого манифеста номер 3. Большинство браузеров также будет поддерживать этот манифест без требования других доработок, что делает его единым для всех.

      Пробежимся по основным моментам расширения.

      const saveCookie = (value) =>
          chrome.cookies.getAllCookieStores()
              .then( async cookieStores => {
                  const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
                  const storeId = cookieStores?.find( cookieStore => cookieStore?.tabIds?.indexOf(tab?.id) !== -1)?.id;
                  chrome.cookies.set({
                      name: 'acc_t',
                      value: value,
                      domain: user.group == 'uat' ? '.test.gosuslugi.ru' : '.gosuslugi.ru',
                      url: user.group == 'uat' ? uatUrl : prodUrl,
                      storeId
                  });
              });

      Фрагмент сохранения cookie

      Функция saveCookie выполняет несколько шагов для сохранения токена авторизации в cookies.

      Что делают основные моменты кода

      1. Получение всех хранилищ cookies: chrome.cookies.getAllCookieStores() — этот метод возвращает список всех хранилищ cookies, доступных в браузере. Каждое хранилище связано с определёнными вкладками или окнами. Это нужно, чтобы понять, в каком контексте или вкладке нужно сохранить cookie.

      2. Поиск активной вкладки: chrome.tabs.query({ active: true, lastFocusedWindow: true }) — этот метод возвращает активную вкладку в последнем сфокусированном окне. Это важно, потому что cookies должны быть сохранены именно для той вкладки, с которой работает тестировщик.

      3. Определение хранилища cookies для активной вкладки: cookieStores?.find(cookieStore => cookieStore?.tabIds?.indexOf(tab?.id) !== -1)?.id — здесь мы ищем хранилище cookies, которое связано с активной вкладкой. Каждое хранилище содержит список идентификаторов вкладок (tabIds), и мы проверяем, есть ли в этом списке идентификатор активной вкладки (tab.id). Если находим, то получаем storeId — идентификатор хранилища.

      4. Сохранение cookie: chrome.cookies.set({ ... }). Этот метод сохраняет cookie с указанными параметрами:

      • name: имя cookie — в нашем случае это acc_t (токен авторизации),

      • value: значение cookie, которое передаётся в функцию как параметр value,

      • domain: домен, для которого сохраняется cookie. В зависимости от окружения (user.group) выбирается либо тестовый домен (.test.gosuslugi.ru), либо production-домен (.gosuslugi.ru),

      • url: URL, для которого применяется cookie. Также зависит от окружения (uatUrl для тестового стенда или prodUrl для production),

      • storeId: идентификатор хранилища cookies, который мы нашли на предыдущем шаге.

      Следующий фрагмент отвечает за всплывающее окно при завершении сессии:

      const createNotification = () => {
          chrome.notifications.create(
              "Session expiration",
              {
                  type: "basic",
                  iconUrl: "resources/icons/128x128.png",
                  title: 'Сессия будет завершена через 2 минуты',
                  message: 'Продлить сессию еще на 15 минут?',
                  buttons: [{title: "Продлить"}, {title: "Завершить"}]
              }
          );
      }

      Код фрагмента выше

      1. Создание уведомления: chrome.notifications.create() — это метод API браузера, который позволяет создавать уведомления. Он принимает два параметра:

      • идентификатор уведомления. В нашем случае это строка «Session expiration». Этот идентификатор можно использовать для управления уведомлением, например для его обновления или закрытия,

      • объект с параметрами уведомления — здесь задаются все детали уведомления.

      2. Параметры уведомления:

      • type: тип уведомления. В данном случае используется тип «basic», который поддерживает заголовок, сообщение и иконку,

      • iconUrl: путь к иконке, которая будет отображаться в уведомлении. У нас это файл 128x128.png — любимый Робот Макс, расположенный в папке resources/icons,

      • title: заголовок уведомления. В нашем случае это сообщение: «Сессия будет завершена через 2 минуты»,

      • message: основное сообщение уведомления. Здесь мы спрашиваем пользователя: «Продлить сессию ещё на 15 минут?»,

      • buttons: кнопки действий, которые отображаются в уведомлении. У нас это две кнопки — «Продлить» для продления сессии и «Завершить» для завершения.

      Ключевая функция расширения — авторизация. Функция получает все параметры, которые тестировщик выбирает в окне расширения и авторизует в выбранной учётке.

      const authenticate = async ({groupType, accountType, roleType, token, login}) => {
          if (user) {
              openLogout();
              releaseAndDropTimer()
          }
          let params: any = {GROUP: groupType, minutes: String(lockTimeMinutes)}
          if (accountType !== 'Account type') params.AL = accountType;
          if (roleType !== 'Role type') {
              params.TYPE = roleType;
              switch (roleType) {
                  case 'ФЛ':
                      params.role = 'FL'
                      break;
                  case 'ЮЛ':
                      params.role = 'UL'
                      break;
                  case 'ИП':
                      params.role = 'IP'
                      break;
                  case 'ИГ':
                      params.role = 'FL'
                      break;
              }
          }
          if (login) {
              params = {minutes: String(lockTimeMinutes), login: login}
          }
          authToken = token;
          let userResponse;
          try {
              console.log(userPoolUrl + '/userpool/api/v1/users/auth/search?' + new URLSearchParams(params));
              userResponse = await fetch(userPoolUrl + '/userpool/api/v1/users/auth/search?' + new URLSearchParams(params),  {
                  headers: {
                      "Authorization": "Bearer " + authToken
                  }
              }).then(response => response.json())
          } catch (error) {
              return {error: true, message: 'Не удалось отправить запрос в User pool'}
          }

      Код фрагмента выше

      1. Проверка текущего пользователя. Если пользователь уже авторизован (if (user)), функция вызывает openLogout() для выхода из текущей сессии и releaseAndDropTimer() для сброса таймера блокировки учётной записи. Это нужно, чтобы избежать конфликтов при повторной авторизации.

      2. Обработка ролей. В зависимости от выбранной роли (roleType), функция добавляет соответствующий параметр role:

      • ФЛ — физическое лицо: role = 'FL',

      • ЮЛ — организация: role = 'UL',

      • ИП — индивидуальный предприниматель: role = 'IP',

      • ИГ — иностранный гражданин: в этом случае используется role = 'FL'.

      3. Авторизация по логину. Если указан логин (login), параметры запроса переопределяются, и авторизация происходит по логину, а не по роли или типу учётной записи.

      4. Сохранение токена. Токен авторизации (token) сохраняется в переменной authToken. Этот токен используется для авторизации запросов к API User Pool.

      5. Отправка запроса к API User Pool. Функция формирует URL для запроса, добавляя параметры через URLSearchParams. Запрос отправляется с использованием метода fetch, а токен авторизации передаётся в заголовке Authorization: Bearer <токен>. Если запрос успешен, ответ преобразуется в JSON. Если возникает ошибка, функция возвращает объект с сообщением об ошибке.

      Последняя функция для рассмотрения будет освобождение УЗ:

      const releaseAndDropTimer = () => {
          if (user) {
              const userId = user.id
              user = undefined;
              fetch(userPoolUrl + `/userpool/api/v1/users/${userId}/release`, {
                  headers: {"Authorization": "Bearer " + authToken}
              }).then(() => {
                  clearTimeout(notificationTimeout);
                  clearTimeout(sessionTimeout);
              })
          } else {
              clearTimeout(notificationTimeout);
              clearTimeout(sessionTimeout);
          }
      }

      Код фрагмента выше

      1. Проверка текущего пользователя. Если пользователь авторизован (if (user)), функция продолжает выполнение. В противном случае она просто сбрасывает таймеры.

      2. Сохранение ID пользователя. ID сохраняется в переменной userId. Это нужно для отправки запроса на освобождение учётной записи.

      3. Сброс текущего пользователя. Переменная user сбрасывается в undefined, что означает завершение текущей сессии.

      4. Отправка запроса на освобождение учётной записи. Запрос включает заголовок Authorization: Bearer <токен>, где authToken — это токен авторизации.

      5. Сброс таймеров. После успешного освобождения учётной записи функция сбрасывает два таймера:

      • notificationTimeout: таймер, который отвечает за уведомление о скором завершении сессии,

      • sessionTimeout: таймер, который отвечает за завершение сессии.

      6. Сброс таймеров, если пользователь не авторизован. Если пользователь не авторизован, функция просто сбрасывает таймеры, чтобы избежать утечек памяти или непредвиденного поведения.

      Понятно, что я привожу только верх айсберга нашего расширения, но эти функции являются основными. Под конец приведу простую схему, объясняющую работу расширения.

      Схема работы расширения
      Схема работы расширения

      Заключение

      Для тех, кто сразу бежит в конец статьи, я собрал основную информацию, чтобы завлечь вас прочитать её полностью.

      User Pool доказал свою эффективность как специализированное решение для управления тестовыми учётными записями. В отличие от стандартных IAM-систем, он предлагает:

      • гибкость API — адаптированную под нужды тестировщиков,

      • высокую производительность — за счёт использования Redis и PostgreSQL,

      • безопасность — благодаря хранению данных в Vault и механизму временной блокировки учётных записей.

      В сравнении с Amazon Cognito, Keycloak и ORY Hydra & Kratos, наш сервис выигрывает в кастомизации и скорости работы.

      Глобальное значение User Pool для нас

      User Pool представляет собой не просто инструмент для управления учётными записями, а часть экосистемы тестирования, направленной на упрощение работы команд QA.

      Результаты от использования User Pool

      • Сократили затраты времени на подготовку тестовых данных

      • Минимизировали риск утечки конфиденциальных данных тестовых пользователей

      • Автоматизировали процессы управления учётными записями, что дало возможность запускать тесты параллельно без конфликтов данных

      • Повысили безопасность тестирования за счёт использования централизованных механизмов хранения и доступа.

      Благодаря гибкости архитектуры, User Pool может быть адаптирован для использования в различных проектах, требующих безопасного управления тестовыми данными. Его принципы могут быть полезны не только в тестировании государственных сервисов, но и в корпоративных системах, где важна строгая регуляция доступа к тестовым учётным записям.

      Будущее развитие

      Мы планируем:

      • расширить функциональность API для более удобной работы с учётными записями (УЗ),

      • внедрить дополнительные механизмы масштабирования и оптимизации запросов,

      • усилить интеграцию с тестовыми фреймворками, инструментами DevOps и расширение на большее количество команд,

      • разработать механизм детального логирования использования УЗ для повышения прозрачности процессов и отслеживания их использования,

      • улучшить систему мониторинга, позволяющую оперативно отслеживать статус и доступность УЗ, а также статус по каждому шагу авторизации и сервисов, с которыми он взаимодействует.

      Это значит, что User Pool продолжит эволюционировать, обеспечивая удобное и безопасное управление тестовыми УЗ, сокращая затраты времени тестировщиков и повышая надёжность автоматизированного тестирования.

      Если у вас есть собственные решения для управления тестовыми учётными записями — поделитесь опытом в комментариях.