Использование request.auth.credentials

request.auth.credentials — это центральная точка доступа к данным аутентифицированного пользователя в Hapi.js. Именно сюда попадает объект, возвращаемый стратегией аутентификации после успешной валидации. В контексте Iron (через механизмы защищённого хранения данных, например, в cookie-based стратегиях) этот объект часто является результатом расшифровки и распаковки защищённого payload.


Каждый HTTP-запрос в Hapi.js получает объект request, внутри которого создаётся namespace аутентификации:

  • request.auth.isAuthenticated — признак успешной аутентификации
  • request.auth.strategy — имя использованной стратегии
  • request.auth.credentials — данные пользователя (основной объект)
  • request.auth.artifacts — дополнительные данные, связанные со стратегией

Ключевая часть здесь — credentials. Это уже нормализованный объект, который возвращает функция validate() в стратегии.


Как формируется request.auth.credentials

Данные попадают в request.auth.credentials не «из воздуха», а через pipeline аутентификации.

Типичный поток выглядит так:

  1. Клиент отправляет запрос (например, с cookie или Authorization header)
  2. Стратегия аутентификации извлекает данные (JWT, session, cookie)
  3. Если используется Iron (например, через @hapi/iron), данные предварительно дешифруются
  4. Вызывается validate() функция стратегии
  5. Возвращаемый объект становится request.auth.credentials

Пример стратегии:

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 становится доступен в каждом обработчике запроса.


Использование request.auth.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.


Связь с Iron и защищёнными данными

@hapi/iron используется для сериализации и шифрования данных, которые могут попадать в cookie или session storage. В cookie-based стратегиях Hapi.js данные часто проходят следующий путь:

  • объект credentials сериализуется
  • затем шифруется через Iron seal
  • сохраняется в cookie
  • при следующем запросе происходит unseal (расшифровка)
  • результат снова становится request.auth.credentials

Примерно это выглядит так:

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.


Разница между credentials и artifacts

request.auth.credentials часто путают с request.auth.artifacts.

credentials:

  • идентификационные данные пользователя
  • используются для авторизации (roles, permissions)
  • доступны в бизнес-логике приложения

artifacts:

  • служебные данные стратегии
  • могут содержать токены, raw session, metadata
  • не предназначены для прямой бизнес-логики

Пример:

{
  credentials: {
    id: 1,
    role: 'admin'
  },
  artifacts: {
    tokenId: 'abc123',
    expiresAt: 1710000000
  }
}

Типичные ошибки при работе с credentials

1. Предположение о наличии объекта

// Ошибка
const id = request.auth.credentials.id;

Если маршрут не защищён, credentials будет undefined. Правильно:

if (!request.auth.isAuthenticated) {
  throw Boom.unauthorized();
}

2. Хранение лишних данных

credentials часто используют как «свалку» данных. Это приводит к проблемам:

  • увеличение размера cookie (если используется Iron sealing)
  • утечки чувствительной информации
  • сложность обновления сессии

Правильный подход — хранить только идентификатор и минимальные роли:

credentials: {
  id: user.id,
  scope: ['read', 'write']
}

3. Изменение credentials в runtime

request.auth.credentials считается immutable в рамках запроса. Изменения:

request.auth.credentials.role = 'admin';

могут привести к рассинхронизации логики авторизации. Если требуется обновление — используется пересоздание сессии или токена.


Использование scope и permissions через credentials

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.


Передача кастомных данных через credentials

Иногда требуется расширить объект пользователя дополнительной информацией:

return {
  valid: true,
  credentials: {
    id: user.id,
    username: user.username,
    permissions: user.permissions,
    profile: {
      theme: 'dark',
      language: 'ru'
    }
  }
};

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


Влияние Iron-sealing на структуру credentials

При использовании Iron важно учитывать:

  • объект должен быть сериализуемым (JSON-safe)
  • функции, классы и Date объекты могут быть потеряны или преобразованы
  • размер данных напрямую влияет на размер cookie
  • изменение структуры требует миграции сессий

Практический подход:

// допустимо
credentials: {
  id: 1,
  role: 'user'
}

// нежелательно
credentials: {
  id: 1,
  userObject: fullDatabaseRecord // слишком тяжёлый объект
}

Контекст использования внутри lifecycle methods

credentials доступны не только в handler, но и в lifecycle hooks:

server.ext('onPreHandler', (request, h) => {

  if (request.auth.isAuthenticated) {
    const userId = request.auth.credentials.id;
  }

  return h.continue;
});

Это позволяет реализовывать централизованные проверки доступа.


Связь с stateless и stateful аутентификацией

В stateless системах (JWT):

  • credentials формируются из payload токена
  • Iron обычно не используется напрямую

В stateful системах (cookie/session):

  • credentials часто восстанавливаются из зашифрованной сессии
  • Iron играет ключевую роль в защите данных

request.auth.credentials — это точка, где результат всей аутентификационной цепочки становится доступным приложению. Через него проходит управление доступом, идентификация пользователя и связывание бизнес-логики с безопасным контекстом запроса.