request.auth.credentials — это центральная точка доступа к данным аутентифицированного пользователя в Hapi.js. Именно сюда попадает объект, возвращаемый стратегией аутентификации после успешной валидации. В контексте Iron (через механизмы защищённого хранения данных, например, в cookie-based стратегиях) этот объект часто является результатом расшифровки и распаковки защищённого payload.
Каждый HTTP-запрос в Hapi.js получает объект request, внутри которого создаётся namespace аутентификации:
Ключевая часть здесь — credentials. Это уже нормализованный объект, который возвращает функция validate() в стратегии.
Данные попадают в request.auth.credentials не «из воздуха», а через pipeline аутентификации.
Типичный поток выглядит так:
Пример стратегии:
server.auth.strategy('session', 'cookie', {
cookie: {
name: 'sid',
password: 'very_secure_password_that_is_at_least_32_chars',
isSecure: true
},
validateFunc: async (request, session) => {
const account = await Users.findById(session.id);
if (!account) {
return { valid: false };
}
return {
valid: true,
credentials: {
id: account.id,
username: account.username,
role: account.role
}
};
}
});
После успешного прохождения validateFunc объект credentials становится доступен в каждом обработчике запроса.
Внутри route handler доступ к данным пользователя осуществляется напрямую:
server.route({
method: 'GET',
path: '/profile',
handler: (request, h) => {
const userId = request.auth.credentials.id;
const username = request.auth.credentials.username;
return {
message: 'Профиль пользователя',
user: {
id: userId,
username
}
};
}
});
Важно понимать: request.auth.credentials гарантированно существует только при request.auth.isAuthenticated === true.
@hapi/iron используется для сериализации и шифрования данных, которые могут попадать в cookie или session storage. В cookie-based стратегиях Hapi.js данные часто проходят следующий путь:
Примерно это выглядит так:
const Iron = require('@hapi/iron');
const sealed = await Iron.seal(
{ id: 123, role: 'admin' },
'very_secure_password_that_is_at_least_32_chars',
Iron.defaults
);
const unsealed = await Iron.unseal(
sealed,
'very_secure_password_that_is_at_least_32_chars',
Iron.defaults
);
В Hapi этот процесс скрыт внутри auth strategies, но логически именно он приводит к появлению credentials в request.
request.auth.credentials часто путают с request.auth.artifacts.
credentials:
artifacts:
Пример:
{
credentials: {
id: 1,
role: 'admin'
},
artifacts: {
tokenId: 'abc123',
expiresAt: 1710000000
}
}
// Ошибка
const id = request.auth.credentials.id;
Если маршрут не защищён, credentials будет undefined. Правильно:
if (!request.auth.isAuthenticated) {
throw Boom.unauthorized();
}
credentials часто используют как «свалку» данных. Это приводит к проблемам:
Правильный подход — хранить только идентификатор и минимальные роли:
credentials: {
id: user.id,
scope: ['read', 'write']
}
request.auth.credentials считается immutable в рамках запроса. Изменения:
request.auth.credentials.role = 'admin';
могут привести к рассинхронизации логики авторизации. Если требуется обновление — используется пересоздание сессии или токена.
Hapi поддерживает встроенную модель доступа через scope, которая хранится внутри credentials:
credentials: {
id: 1,
scope: ['admin', 'user']
}
В маршруте:
server.route({
method: 'DELETE',
path: '/admin/users',
options: {
auth: {
strategy: 'session',
scope: ['admin']
},
handler: (request, h) => {
return { status: 'ok' };
}
}
});
Hapi автоматически проверяет request.auth.credentials.scope.
Иногда требуется расширить объект пользователя дополнительной информацией:
return {
valid: true,
credentials: {
id: user.id,
username: user.username,
permissions: user.permissions,
profile: {
theme: 'dark',
language: 'ru'
}
}
};
После этого данные доступны во всех маршрутах без дополнительных запросов к базе.
При использовании Iron важно учитывать:
Практический подход:
// допустимо
credentials: {
id: 1,
role: 'user'
}
// нежелательно
credentials: {
id: 1,
userObject: fullDatabaseRecord // слишком тяжёлый объект
}
credentials доступны не только в handler, но и в lifecycle hooks:
server.ext('onPreHandler', (request, h) => {
if (request.auth.isAuthenticated) {
const userId = request.auth.credentials.id;
}
return h.continue;
});
Это позволяет реализовывать централизованные проверки доступа.
В stateless системах (JWT):
В stateful системах (cookie/session):
request.auth.credentials — это точка, где результат всей аутентификационной цепочки становится доступным приложению. Через него проходит управление доступом, идентификация пользователя и связывание бизнес-логики с безопасным контекстом запроса.