Задача: Интегрировать MODX frontend-сессии в auth-контекст haih-agent
Интегрировать MODX frontend-сессии в auth-контекст haih-agent
Научить новый frontend Kilfor определять текущего пользователя напрямую по существующей MODX-сессии без зависимости от legacy HTTP endpoint.
Контекст
Сессии пользователей Kilfor сейчас хранятся и обслуживаются MODX. Новый frontend на haih-agent уже реализует часть прикладной логики самостоятельно, поэтому ему периодически нужна детальная информация о текущем авторизованном пользователе.
В MODX есть внутренний PHP API для работы с текущим пользователем внутри MODX runtime, но штатного внешнего HTTP endpoint уровня current user для frontend-контекста из коробки нет. В modSociety существует собственный endpoint для получения пользователя, однако новый frontend не должен зависеть от этого legacy-компонента.
Цель
Реализовать в haih-agent инфраструктурный слой, который умеет по существующей MODX session cookie определить пользователя, авторизованного в заданном frontend-контексте (web или другом), и предоставить нормализованные данные пользователя прикладному коду нового frontend.
Что исследовать и реализовать
- Разобраться с фактическим форматом хранения frontend-сессий в используемой версии MODX и на Kilfor.
- Определить, как из session id и данных
modx_sessionполучить пользователя для конкретного context key. - Корректно разбирать PHP session serialization, не привязываясь к regex-хаку.
- Получать необходимые данные пользователя из
modUser,modUserProfileи проектных связей. - Сделать MODX-specific реализацию отдельным адаптером/провайдером, чтобы прикладной код работал с абстракцией
currentUser, а не знал структуру MODX session. - Не зашивать
webв низкоуровневый parser: context key должен задаваться конфигурацией или вызывающим auth-слоем. - Учесть истечение/инвалидность сессии и отсутствие авторизации в запрашиваемом контексте.
- Не переносить целиком MODX ACL/security runtime без отдельной необходимости; на первом этапе задача — достоверно определить пользователя и его данные.
- Предусмотреть возможность другого session storage/handler в будущем, не связывая весь auth-код напрямую с SQL к
modx_session. - Покрыть parser и определение пользователя fixture/integration-тестами на реальных примерах MODX frontend-сессий.
Ожидаемый результат
В GraphQL/request context haih-agent доступен нормализованный текущий пользователь для указанного frontend-контекста, полученный напрямую из существующей MODX-сессии. Для этого не требуется отдельный запрос к legacy endpoint modSociety или bootstrap MODX на каждый запрос нового frontend.
Ворклоги
Итог работы
Задача по чтению существующих MODX frontend-сессий на стороне haih-agent практически решена и может считаться выполненной.
Что проверено
На стороне нового frontend удалось напрямую читать данные MODX-сессии без bootstrap MODX и без обращения к legacy endpoint modSociety. Это подтверждает саму архитектурную возможность использовать MODX как текущее хранилище сессий, а разбор и дальнейшую работу с данными выполнять уже внутри haih-agent.
Отдельно выяснилось, что основная техническая проблема оказалась не в чтении записи сессии из базы, а в десериализации её PHP session payload в Node.js.
Проблема с php-serialize
php-serialize не решает задачу напрямую, потому что MODX/PHP-сессия в используемом формате — это не просто один результат serialize($_SESSION). Поэтому библиотека не подходит как готовый session decoder для текущего кейса.
Использование php-session-unserialize
Для разбора сессии был взят пакет php-session-unserialize. Он корректно читает сам формат PHP session, но обнаружилась особенность реализации библиотеки: PHP associative arrays внутри readArray() всегда создаются как JavaScript Array.
По сути библиотека делает следующее:
const resultArray = []
resultArray[key] = value
Если PHP-массив содержит строковый ключ, например mgr, в JavaScript получается массив с именованным свойством (arr.mgr = ...). В console.log такие данные видны, но JSON.stringify и GraphQL сериализуют только индексированные элементы массива, поэтому именованные свойства теряются.
Практический пример: значение MODX-сессии вида
modx.user.0.resourceGroups => { mgr: [] }
после исходного парсинга выглядело в логах корректно, но через GraphQL превращалось в:
"modx.user.0.resourceGroups": []
Реализованный workaround
После unserialize() добавлена рекурсивная нормализация результата:
function convertArraysToObjects(obj: unknown): unknown {
if (Array.isArray(obj)) {
const keys = Object.keys(obj)
const hasStringKeys = keys.some((k) => isNaN(Number(k)))
if (hasStringKeys) {
const result: Record<string, unknown> = {}
for (const key of keys) {
result[key] = convertArraysToObjects(
(obj as unknown as Record<string, unknown>)[key],
)
}
return result
}
return obj.map(convertArraysToObjects)
}
if (obj && typeof obj === 'object') {
const result: Record<string, unknown> = {}
for (const [key, value] of Object.entries(obj)) {
result[key] = convertArraysToObjects(value)
}
return result
}
return obj
}
После этого PHP associative arrays с именованными ключами преобразуются в обычные JS objects и корректно проходят через JSON/GraphQL.
Результат
После нормализации данные MODX-сессии читаются корректно, включая вложенные структуры. В частности, успешно получаются такие ветки:
{
"modx.user.0.resourceGroups": {
"mgr": []
},
"modx.user.0.attributes": {
"web": {
"modAccessContext": {
"web": [
{
"principal": 0,
"authority": "0",
"policy": {
"load": true,
"formit": true,
"formit_encryptions": false
}
}
]
}
}
}
}
Также в сессии видны данные для других пользователей/контекстов, например modx.user.1.attributes и ACL-структуры с большим набором manager permissions. То есть session payload теперь доступен целиком и без потерь при GraphQL-сериализации.
Ограничения текущего решения
Теоретически текущий converter может неоднозначно преобразовать PHP-массив со смешанными числовыми и строковыми ключами: при наличии хотя бы одного строкового ключа весь JS Array превращается в Object. Для обычных структур MODX-сессий это сейчас не критично; реальные данные, необходимые в проекте, после нормализации приходят корректно.
Поэтому на данном этапе нет необходимости писать собственный PHP session parser или форкать библиотеку. Если позже встретится реальный MODX session payload, который текущая схема разбирает неверно, это можно вынести в отдельную техническую задачу.
Граница выполненной задачи
Цель этой задачи была именно в принципиальной возможности читать и корректно десериализовать существующую MODX frontend-сессию на стороне haih-agent. Эта цель достигнута.
Логика определения конкретного текущего пользователя по содержимому сессии, выбор нужного frontend context, получение дополнительных данных пользователя и дальнейшее построение auth/currentUser API — это следующий прикладной слой и при необходимости должен оформляться отдельно.