Введение
LoQ (Login via QR) - это функция для идентификации и аутентификации пользователя на веб-сайте исключительно с помощью QR-кода на странице входа. Пользователю не нужно вводить логин, пароль или какие-либо одноразовые пароли.
Пользователю нужно лишь отсканировать QR-код с помощью мобильного приложения и подтвердить операцию входа в систему.
В то же время пользователь будет идентифицирован по своему идентификатору PC из мобильного приложения, а аутентификация будет осуществляться с помощью цифровой подписи и функции подтверждения транзакций в PC.
Как это работает можно посмотреть на демо-портале PC.

Процесс идентификации и аутентификации
В процессе идентификации и аутентификации пользователя с использованием функции LoQ участвуют как минимум 8 компонентов.
В результате бэкенд веб-приложения получит от компонентов LoQ идентификатор пользователя PC и идентификатор транзакции PC идентифицированного и аутентифицированного пользователя.
После этого веб-приложение может сопоставить идентификатор пользователя PC с его записью в базе данных и приступить к авторизации.
Предварительные требования
- Функция LoQ должна быть активирована и установлена в инфраструктуре заказчика (либо может использоваться облачная установка)
- Веб-приложение должно быть связано с системой PC (пользователи веб-приложения должны использовать PC для подтверждения транзакций).
Описание процесса
Диаграмма процесса показана на рисунке 1 (рекомендуется увеличить изображение).

Участники процесса
| Name | Functions |
|---|---|
| Бэкенд веб-приложения | Бэкенд веб-приложения обрабатывает запросы пользователей. В рабочем процессе должен запрашивать LoQ JS у компонента LoQ Internal, размещать его на странице входа и предоставлять пользователю |
| Веб-браузер | Веб-браузер на стороне пользователя обрабатывает LoQ JS, отображает QR-код для входа и, после успешной идентификации и аутентификации пользователя, перенаправляет его в авторизованную зону |
| LoQ JS (в браузере) | JavaScript-скрипт, который выполняет следующие действия: - запрашивает QR-код и параметры сессии у LoQ External с заданным интервалом (по умолчанию — каждую 1 минуту) - проверяет у LoQ External, был ли пользователь уже идентифицирован и аутентифицирован, с заданным интервалом (по умолчанию — каждую секунду) - получает и перерисовывает QR-код на странице входа или выполняет перенаправление на ссылку авторизации веб-приложения после успешной аутентификации |
| LoQ External | Компонент для взаимодействия с LoQ JS и PC Mobile SDK. Должен быть размещён в демилитаризованной зоне (DMZ) с доступом из/в Интернет |
| LoQ Internal | Компонент для взаимодействия с бэкендом веб-приложения и PC Server. Генерирует и обрабатывает параметры сессии во время идентификации и авторизации пользователя. Выполняет callback-запрос к бэкенду веб-приложения для получения PC User ID идентифицированного и аутентифицированного пользователя в сессии. Должен быть размещён во внутренней сети без доступа к Интернету |
| PC Server | Компонент для создания и подтверждения транзакции с целью аутентификации пользователя |
| Мобильное приложение | Вызывает методы PC Mobile SDK, предоставляет пользовательский интерфейс на мобильном телефоне |
| PC Mobile SDK | Обрабатывает рабочий процесс LoQ на стороне мобильного устройства: - считывает значение QR-кода с параметрами сессии - отправляет запрос к LoQ External с указанием идентификатора пользователя PC - обрабатывает подтверждение транзакции (цифровую подпись) для аутентификации пользователя |
Шаги процесса (по схеме)
- Когда бэкенд веб-приложения получает запрос на страницу входа (новый пользователь открывает веб-сессию), он обращается к
LoQ InternalзаLoQ JS, указывая необходимые параметры: PC system_id, текст сообщения для входа, URL для callback-уведомления после идентификации и аутентификации пользователя, а также URL для перенаправления браузера в авторизованную зону.
LoQ JSбудет содержать всю необходимую информацию для взаимодействия сLoQ External, отрисовки QR-кода для входа и проверки статуса аутентификации пользователя.
Бэкенд веб-приложениядолжен реализоватьcallback-эндпоинтпо адресуcallback_urlдля получения данных об идентифицированном и аутентифицированном пользователе и идентификаторе транзакции. На основе этой информацииБэкенд веб-приложениядолжен авторизовать пользователя.Бэкенд веб-приложениядолжен реализовать редирект-эндпоинт по адресуredirect_url. По этому адресу браузер будет перенаправлен скриптомLoQ JSпосле успешной аутентификации. Перед этим будет отправлен callback наcallback_url.
Бэкенд веб-приложениявстраиваетLoQ JSв страницу входа и отправляет её вВеб-браузерпользователя.- С этого момента
LoQ JSначинает взаимодействовать сLoQ External: отрисовывает и обновляет QR-код для входа, а также проверяет статус аутентификации пользователя. - При каждом запросе от
LoQ JSкомпонентLoQ Externalпроверяет данные сессии, при необходимости обновляет QR-код и отправляет команду перенаправления, если пользователь был аутентифицирован. - После отображения QR-кода в
Веб-браузер, пользователь может отсканировать его с помощьюМобильного приложениядля запуска процесса идентификации и аутентификации. - Значение QR-кода (после декодирования) передаётся в
PC Mobile SDK, после чего может быть запущен процесс аутентификации:- вызов метода
PCLogin.importFromJSON(…) - определение, может ли один из зарегистрированных пользователей PC быть использован для входа (пользователи должны быть зарегистрированы в той же системе PC)
- запрос транзакции входа для аутентификации пользователя
- вызов метода
- На основе запроса от
PC Mobile SDKс идентификатором пользователя,LoQ Externalобращается кLoQ Internalдля создания транзакции PC черезPC Server. Эта транзакция должна быть подписана для аутентификации пользователя. - Идентификатор транзакции PC (PC Transaction ID) передаётся через
LoQ InternalиLoQ Externalобратно вPC Mobile SDK. - После этого
PC Mobile SDKзапрашивает данные транзакции и передаёт их вМобильное приложениедля отображения пользователю. - Если пользователь соглашается войти в систему,
Мобильное приложениес помощьюPC Mobile SDKподписывает транзакцию. - После подписания и верификации транзакции,
PC Serverотправляет callback-уведомление вLoQ Internal. LoQ Internalотправляет callback вБэкенд веб-приложенияс указаниемPC User IDиPC Transaction IDв рамках текущей сессии.Бэкенд веб-приложениядолжен по внутренним правилам зафиксировать, что:- в сессии с указанным
session id - пользователь с указанным
pc user id - аутентифицирован путём подписания транзакции с указанным
pc transaction id
- в сессии с указанным
- При следующем запросе от
LoQ JS,LoQ Externalответит, чтоВеб-браузердолжен быть перенаправлен наredirect_urlс параметромsession_id. LoQ JSперенаправляет браузер наredirect_urlБэкенда веб-приложения, иБэкенд веб-приложенияполучает из запросаsession_id.Бэкенд веб-приложенияпроверяет, какой пользователь был идентифицирован и аутентифицирован в сессии с указаннымsession id(шаг 13).- Если пользователь найден,
Бэкенд веб-приложенияавторизует веб-сессию для доступа к защищённой зоне. - Опционально,
Бэкенд веб-приложенияможет получить всю информацию о подтверждении транзакции (значение цифровой подписи, данные мобильного устройства, детали транзакции и т.д.) отPC Server, используя идентификатор транзакции.
Интеграция и API
Получение LoQ JS
Запрос LoQ JS
endpoint: {{loq-internal-url}}/system/loginUser
method: POST
content-type: application/json
Тело запроса
{
"systemId" : "{{pc-system-id}}",
"callback" : "{{callback-url}}",
"redirect" : "{{redirect-url}}",
"sessionTimeout" : 6000,
"qrTimeout" : 600,
"size" : 300,
"text": "Please, confirm your login to the best web site",
"textRenderType": "raw",
"qrUrlPrefix": "paycontrol",
"deepLinkPrefix": "paycontrol",
"deepLinkOnly": false,
"pcTransactionAppExtra": "{\"app_callback_deeplink\": \"https://abs.absnet.loc/login_success\"}"
}
Пример ответа с JS-скриптом и ID сессии
{
"jsScript": "[Здесь будет размещён JS-скрипт (см. пример ниже)]",
"sessionId": "7db47b87-8107-4d25-a967-9210e0d50b20]"
}
JS по умолчанию для LoQ
// LoQ JS Script
getQr = function () {
var xhr = new XMLHttpRequest();
var urlLoq = 'https://dev.paycontrol.org/api5/loqr/ext/site/status';
var body = {
sessionId: "1e9a02b9-8827-4df7-a518-923007430872"
};
var loginQR;
var loginDeepLink;
var isRefreshRequired;
makeRequest();
function makeRequest() {
let data = JSON.stringify(body);
let response;
xhr.open('POST', urlLoq);
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.setRequestHeader('Pragma', 'no-cache');
xhr.send(data);
xhr.onload = function (e) {
if (this.status === 200 || this.status === 201) {
response = JSON.parse(e.target.response);
isRefreshRequired = response.isRefreshRequired;
loginQR = response.loginQR;
loginDeepLink = response.loginDeepLink;
checkQR(response);
console.log(response);
} else {
console.log(e.target.onerror);
}
setTimeout(makeRequest, 1000);
}
;
}
function checkQR(response) {
if (response.redirect != null) {
loginQR = null;
document.getElementById('pc_lo_qr').src = "";
var redirectForm = document.createElement('form');
var sessionId = document.createElement('input');
document.body.append(redirectForm);
redirectForm.append(sessionId);
redirectForm.setAttribute('action', response.redirect.url);
redirectForm.setAttribute('method', 'post');
redirectForm.setAttribute('hidden', 'true');
sessionId.setAttribute('name', 'sessionId');
sessionId.setAttribute('id', 'sessionId');
sessionId.setAttribute('type', 'hidden');
sessionId.setAttribute('value', response.redirect.sessionId);
redirectForm.submit();
}
if (response.isRefreshRequired || typeof (loginQR) == undefined) {
loginQR = response.loginQR;
loginDeepLink = response.loginDeepLink;
document.getElementById('pc_lo_qr').src = "data:image/gif;base64," + loginQR;
if (null != loginDeepLink) {
document.getElementById('pc_lo_deeplink').href = loginDeepLink;
}
}
}
};
Этот запрос генерирует JS-скрипт со всеми необходимыми параметрами для работы на стороне веб-браузера.
| Параметр | Обязательный | Описание |
|---|---|---|
| systemId | да | ID системы PC |
| callback | да | URL для callback-уведомления с информацией об идентифицированном и аутентифицированном пользователе PC |
| redirect | да | Редирект |
| sessionTimeout | да | Время жизни сессии входа в секундах. После истечения этого времени сессия не может быть использована для входа |
| qrTimeout | да | Время жизни QR-кода. После истечения этого времени QR-код будет автоматически сгенерирован компонентом LoQ External |
| size | да | Размер QR-кода в пикселях |
| text | нет | Текст транзакции входа. Этот текст будет использован для создания транзакции PC и должен быть показан пользователю в мобильном приложении. Если текст не указан, будет создана «пустая» транзакция. Рекомендуется включать в текст информацию об ОС, версии браузера и времени входа. |
| textRenderType | нет | Способ отображения текста text в мобильном приложении. Может быть raw или markdown. Значение по умолчанию — raw |
| qrUrlPrefix | нет | Если задано это значение, в QR-код будет закодирован deep-link с указанным протоколом (префиксом). Это будет полезно, если ваше приложение поддерживает deep-links и пользователь использует сканер QR-кодов (например, приложение "Камера" на iOS). Deep-link в QR будет иметь вид [qrUrlPrefix]://loq?login_json=[json to be provided to PC Mobile SDK] |
| deepLinkPrefix | нет | Если задано это значение, помимо QR-кода, LoQ сгенерирует deep-link для отображения на мобильной версии страницы входа. Это позволяет показывать QR-код для десктопной версии и deep-link — для мобильной. Пользователь может тапнуть по deep-link для запуска процесса входа (ваше мобильное приложение должно обрабатывать deep-links) Deep-link будет иметь вид [deepLinkPrefix]://loq?login_json=[json to be provided to PC Mobile SDK] Обратите внимание, что JS будет отдельно размещать QR-код и deep-link в HTML DOM (см. JS по умолчанию). |
| deepLinkOnly | нет | Если true, QR-код не будет сгенерирован — будет предоставлен только deep-link. Полезно при запросе входа из мобильного браузера |
| pcTransactionAppExtra | нет | Это значение будет передано в транзакцию на PC Server в процессе входа и предоставлено мобильному приложению. См. Create Transaction Request Это значение может содержать переменную %SESSION_ID% в дополнение к переменным, заданным на PC Server. |
В ответе возвращается JS-скрипт, который должен быть встроен в страницу входа внутри html-тега <script></script>.
Содержимое страницы входа
Пример JS-кода для запуска LoQ
// если LoQ JS успешно встроен
if (typeof getQr === "function") {
getQr();
}
По умолчанию LoQ JS пытается найти в структуре HTML DOM объект pc_lo_qr типа img и вставить его в значение свойства src полученного QR-кода.
Если задан параметр deepLinkPrefix, то по умолчанию LoQ JS пытается найти объект pc_lo_deeplink типа a и вставить deep-link в свойство href.
Чтобы запустить процесс взаимодействия между LoQ JS и LoQ External, на странице входа должна быть выполнена функция с именем по умолчанию getQr (см. пример).
LoQ JS можно настроить или заменить (при необходимости) в процессе встраивания LoQ. В этом случае названия функций и объектов могут отличаться.
Требования к получателю Callback
Содержимое callback
endpoint: {{callback_url}}
method: POST
content-type: application/json
{
"pc_callback":{
"loqr_callback":{
"sessionId":"25552734-a388-4f30-8517-2b67598f2f75",
"pcUserId":"c1d656e9-481b-4244-ade1-f36ec10eff25",
"transactionId":"bf6c447a-0f82-4bd3-a49f-0c460bea4fcc"
},
"type":"loqr_callback",
"result":{
"error_message":"Success",
"error_code":0
},
"version":3
}
}
После идентификации и аутентификации пользователя LoQ Internal отправит коллбэк на бэкенд веб-приложения по адресу callback_url, указанному в запросе Get LoQ JS.
| Параметр | Обязательный | Описание |
|---|---|---|
| pc_callback.type | да | Значение должно быть loqr_callback |
| pc_callback.loqr_callback | да | Данные об идентифицированном и аутентифицированном пользователе |
| pc_callback.loqr_callback.sessionId | да | ID сессии LoQ, использованной для аутентификации пользователя |
| pc_callback.loqr_callback.pcUserId | да | ID аутентифицированного пользователя в системе PC |
| pc_callback.loqr_callback.transactionId | да | ID транзакции PC, подписанной аутентифицированным пользователем. |
| pc_callback.result | да | Error-описание |
| pc_callback.version | да | Версия callback |
Требования к обработчику редиректа
Стандартный скрипт для выполнения редиректа
// очистить значение QR-кода
document.getElementById('pc_lo_qr').src = "";
// создать HTML-форму
var redirectForm = document.createElement('form');
document.body.append(redirectForm);
redirectForm.setAttribute('action', response.redirect.url);
redirectForm.setAttribute('method', 'post');
redirectForm.setAttribute('hidden', 'true');
// добавить поле с именем sessionId
var sessionId = document.createElement('input');
sessionId.setAttribute('name', 'sessionId');
sessionId.setAttribute('id', 'sessionId');
sessionId.setAttribute('type', 'hidden');
// установить значение sessionId, полученное от LoQ External
sessionId.setAttribute('value', response.redirect.sessionId);
redirectForm.append(sessionId);
// отправить форму для выполнения редиректа
redirectForm.submit();
После того как LoQ JS получит информацию об аутентификации пользователя, он перенаправит веб-браузер на redirect_url, указанный в запросе Get LoQ JS.
Стандартный метод редиректа LoQ - это js-скрипт, который встраивает html-форму в модель HTML DOM и отправляет ее.
Перед этим скрипт удаляет значение QR-кода.
Таким образом, если вы используете метод редиректа по умолчанию, получатель редиректа на стороне бэкенда веб-приложения должен обрабатывать запрос с типом содержимого application/x-www-form-urlencoded.
JS-скрипт для редиректа можно настроить или заменить при необходимости в процессе установки LoQ.
Требования к мобильному приложению
Мобильное приложение должно реализовывать процесс LoQ в соответствии со схемой.
Процесс входа в систему с точки зрения мобильного приложения управляется классом PCLogin.
Пример алгоритма входа в систему с помощью QR-кода (или deep-link):
- Отсканируйте QR-код (или воспользуйтесь deep-link) и извлечь JSON, содержащий все необходимые данные
- Вызовите
importFromJson(String, GetLoginDataCallback)для создания объектаPCLogin. Созданный объект будет возвращен в качестве параметра методаsuccess(). - Вызовите
suggestUsers()для созданного объекта, чтобы получить ключи, которые можно использовать для входа в систему - После выбора ключа вызовите
requestLoginTransaction(PCUser, GetLoginTransactionCallback)с выбраннымPCUserиз списка, предложенного функциейsuggestUsers() - После вызова
PCLogin.GetLoginTransactionCallback.success(PCTransaction)вы можете отобразить данные транзакции в приложении. Обратите внимание, чтоPCTransaction.getTransactionText()может возвращать либо null, либо текст транзакции. Не пытайтесь подписать эту транзакцию вручную. Вместо этого вызовитеconfirm(PCGeneralCallback)для завершения входа в систему илиdecline(PCGeneralCallback)для отмены входа.
Более подробную информацию можно найти в PC Mobile SDK.
Также можно использовать мобильное приложение PC в качестве примера.
Интеграция в инфраструктуру
Взаимодействие компонентов
LoQ состоит из двух частей: LoQ External и LoQ Internal.
Функции этих элементов есть описаны в разделе "Рабочий процесс".
Схема взаимодействия участников процесса входа в систему представлена на рисунке 2.

Варианты поставки серверных компонентов
Компоненты сервера LoQ поставляются в виде Java-приложения (jar-файла) и инструментов для запуска в качестве службы (на базе Linux или Windows).
LoQ использует сервер кэширования Redis для хранения данных об активных сеансах. Redis должен быть установлен, чтобы к нему можно было получить доступ из внутренних и внешних компонентов LoQ.
Типовая конфигурация сервера
Типовой сервер (или контейнер) включает следующие установленные компоненты:
| Компонент | Описание |
|---|---|
| Операционная система | На базе Linux |
| Рабочая среда сервера приложений | Java 8/11 |
Все компоненты LoQ запускаются автоматически вместе с операционной системой. Ручная настройка запуска/остановки не требуется.
Если не предоставляются ни физические, ни виртуальные серверы, подготовка ОС выполняется заказчиком. Подготовка включает:
- установку операционной системы;
- корректную настройку записей DNS;
- установку Java Runtime Environment;
- подготовку TLS-сертификатов (при необходимости).
Могут использоваться следующие альтернативные компоненты:
| Компонент | Описание |
|---|---|
| Операционная система | Microsoft Windows |
| Рабочая среда сервера приложений | Java 8/11 |
Отказоустойчивость и масштабирование
Возможности отказоустойчивости и масштабирования аналогичны компонентам PC Server.
Установка и запуск
Перед установкой
Сначала необходимо развернуть и предоставить доступ к одной и той же базе данных Redis для компонентов Internal и External LoQ.
Настраиваемые параметры
Ниже приведены примеры файлов application.yml. Все параметры, кроме logging, должны быть заданы:
LoQR Int
Пример application.yml для LoQR Internal
server:
port: 8098
address: 0.0.0.0
loqr:
parametrs:
qr:
width: 120
typeImg: "gif"
pc:
url: "http://pc:8080/pc-api/"
url: "http://loqrint:8098"
urlExternal: "https://example.com/loqr/ext"
timeouts:
QR: 120
session: 600
spring:
redis:
host: redis
port: 6379
database: 1
jedis:
pool:
min-idle: 5
max-active: 100
max-idle: -1
max-wait: 10000
#logging:
# level:
# tech.paycon: DEBUG
- loqr.pc.url — эндпоинт API PCS;
- loqr.url — адрес LoQR Internal. Используется для получения коллбэка от PC при подтверждении транзакции входа;
- loqr.urlExternal — Внешний адрес LoQR. Используется в браузере конечного пользователя для получения статуса сеанса и обновления QR-кода;
- spring.redis.host — Redis host;
- spring.redis.database — Redis DB.
LoQR Ext
Пример application.yml для LoQR External
server:
port: 8099
address: 0.0.0.0
loqrExternal:
loqr: "http://loqrint:8098"
logoQRPath: "pc_logo.png"
url: "https://example.com/loqr/ext"
logoOn: true
spring:
redis:
host: redis
port: 6379
database: 1
jedis:
pool:
min-idle: 10
max-active: 60
max-idle: -1
max-wait: -1
# logging:
# level:
# tech.paycon: DEBUG
- loqrExternal.loqr — адрес Internal LoQR. Используется Internal LoQR для создания транзакции входа;
- loqrExternal.url — адрес External LoQR. Этот адрес будет добавлен в QR-код для вызова LoQR External из Mobile SDK при создании транзакции входа;
- spring.redis.host — Redis host;
- spring.redis.database — Redis DB.
Запуск в контейнерах
Образы включают JRE 17 и JAR-файлы модулей LoQR.
Образы
- LoQR Internal: registry.paycontrol.org/loqr/pc_loqr_int:1.0
- LoQR External: registry.paycontrol.org/loqr/pc_loqr_ext:1.0
Переменные окружения
Обязательные переменные окружения, которые необходимо передать контейнерам (или смонтировать файл application.yml):
LoQR Int
LOQR_PC_URL— эндпоинт API PCS;LOQR_URL— адрес LoQR Internal. Используется для callback от системы PC при подтверждении транзакции входа;LOQR_URLEXTERNAL— адрес LoQR External. Используется JS в браузере конечного пользователя для опроса статуса сессии и обновлений QR;SPRING_REDIS_HOST— Redis host;SPRING_REDIS_DATABASE— Redis DB.
LoQR Ext
LOQREXTERNAL_LOQR— адрес Internal LoQR. Используется Internal LoQR для создания транзакции входа;LOQREXTERNAL_URL— адрес External LoQR. Этот адрес будет добавлен в QR-код для вызова LoQR External из Mobile SDK при создании транзакции входа;SPRING_REDIS_HOST— Redis host;SPRING_REDIS_DATABASE— Redis DB.
Добавление доверенных сертификатов
Для добавления доверенных сертификатов, например корневых сертификатов УЦ, нужно передать имя файла каждого сертификата в переменную окружения TRUST_CERT через "точку с запятой".
Добавление сертификатов ЦС
volumes:
- ca-certs-storage:/certificates:ro
environment:
- TRUST_CERT=/certificates/OwnCA.crt;/certificates/testCA.crt