Механизм server.auth.scheme в Hapi используется для
регистрации низкоуровневого способа аутентификации. Схема определяет
поведение проверки учетных данных, но не привязывается к конкретной
стратегии использования. Это слой, на котором описывается логика
проверки запроса, извлечения и валидации данных, а также формирования
результата аутентификации.
Схема становится строительным блоком для стратегий, которые уже применяются к маршрутам. В отличие от стратегии, схема не содержит бизнес-конфигурации, а лишь описывает алгоритм проверки.
Регистрация схемы выполняется через метод:
server.auth.scheme(name, schemeFactory);
name — уникальное имя схемыschemeFactory — функция, возвращающая объект с методом
authenticateФабрика схемы вызывается один раз при регистрации, и должна вернуть объект, реализующий интерфейс аутентификации.
Минимальная структура:
server.auth.scheme('custom', (server, options) => {
return {
authenticate: async (request, h) => {
return h.authenticated({ credentials: {} });
}
};
});
Фабрика схемы получает доступ к серверу и опциям:
(server, options) => { }
server позволяет использовать конфигурацию,
логирование, методы криптографииoptions содержит параметры стратегии, переданные при
созданииЭто позволяет отделить реализацию схемы от конкретных параметров окружения.
Ключевой частью схемы является метод authenticate:
authenticate: async (request, h) => { }
Он должен:
h.authenticated() или отклонить
запросБиблиотека @hapi/iron используется для упаковки и защиты
данных (sealing/unsealing). Это особенно полезно при работе с токенами,
cookie-сессиями или подписанными структурами.
Основные операции:
Iron.seal(data, password, options)Iron.unseal(sealed, password, options)Пример использования внутри схемы:
const Iron = require('@hapi/iron');
server.auth.scheme('iron-scheme', (server, options) => {
return {
authenticate: async (request, h) => {
const cookie = request.state.session;
if (!cookie) {
throw Boom.unauthorized('Missing session');
}
let credentials;
try {
credentials = await Iron.unseal(
cookie,
options.password,
Iron.defaults
);
}
catch (err) {
throw Boom.unauthorized('Invalid session');
}
return h.authenticated({ credentials });
}
};
});
После регистрации схемы она не используется напрямую. Необходимо создать стратегию:
server.auth.strategy('session', 'iron-scheme', {
password: 'super-secure-password'
});
'session' — имя стратегии'iron-scheme' — зарегистрированная схемаДалее стратегия может быть применена к маршрутам:
server.route({
method: 'GET',
path: '/profile',
options: {
auth: 'session'
},
handler: (request, h) => {
return request.auth.credentials;
}
});
При запросе к защищенному маршруту выполняется последовательность:
authenticateIron.unsealrequest.authОшибка аутентификации должна возвращаться через исключение:
throw Boom.unauthorized('Reason');
или через возврат неуспешного результата:
return h.unauthenticated(Boom.unauthorized('Invalid token'));
Важно различать:
Iron требует согласованной конфигурации:
const options = {
password: 'key',
integrity: {
salt: 'random-salt',
iterations: 1000
},
ttl: 24 * 60 * 60 * 1000
};
Эти параметры влияют на:
const Iron = require('@hapi/iron');
const Boom = require('@hapi/boom');
server.auth.scheme('iron-session', (server, options) => {
return {
authenticate: async (request, h) => {
const token = request.state.auth;
if (!token) {
throw Boom.unauthorized('No auth token');
}
let session;
try {
session = await Iron.unseal(
token,
options.password,
Iron.defaults
);
}
catch (err) {
throw Boom.unauthorized('Token invalid or expired');
}
return h.authenticated({
credentials: session,
artifacts: token
});
}
};
});
Схема может включать дополнительную логику обновления токена:
if (session.exp < Date.now()) {
throw Boom.unauthorized('Session expired');
}
или автоматическую переупаковку:
const renewed = await Iron.seal(session, options.password, Iron.defaults);
artifacts позволяют сохранить исходные данные запроса
аутентификации:
return h.authenticated({
credentials: session,
artifacts: token
});
Это полезно для:
Схема должна оставаться изолированной от:
Её задача ограничена:
Отсутствие обработки исключений Iron
Любой unseal без try/catch приводит к падению
запроса.
Хранение логики приложения в схеме
Схема не должна обращаться к базе данных без необходимости.
Неправильное использование options
Параметры схемы должны быть неизменяемыми во время выполнения.
Hapi позволяет регистрировать несколько стратегий на основе одной схемы:
server.auth.strategy('sessionA', 'iron-session', { password: 'A' });
server.auth.strategy('sessionB', 'iron-session', { password: 'B' });
Каждая стратегия использует один и тот же алгоритм, но разные ключи или параметры.
Схема представляет собой чистую функцию проверки запроса, расширенную возможностью использования криптографических механизмов Iron. Она является фундаментом для построения стратегий аутентификации, обеспечивая гибкость и переиспользуемость логики проверки учетных данных.