Как хранить httpOnly cookie после перенаправления с пользовательской SSO?

Вопрос или проблема

Контекст

Существует два приложения:

  • auth-server – провайдер идентификации – это мое собственное решение для единого входа (SSO).
  • img-server – провайдер услуг – это приложение имеет некоторые маршруты, которые я хотел бы аутентифицировать.

auth-server имеет два конечных пункта:

  • GET / – пример URL: https://auth.domain.com/?redirect=https://img.domain.com/getdata
    • Имеет промежуточное ПО, которое проверяет токен из cookies, если он существует.
      • Если токен существует и действителен, он создает публичный токен (который может быть проверен провайдером услуг) и перенаправляет пользователя на URL, указанный в параметре запроса redirect, включая публичный токен как параметр запроса в перенаправлении (authToken).
    • Показывает страницу входа, если промежуточное ПО не возвращает.
  • POST / – пример URL: https://auth.domain.com/?redirect=https://img.domain.com/getdata
    • Использует переданное имя пользователя и пароль для попытки входа
      • Если успешно, сервер сохраняет приватный токен в cookies и генерирует публичный токен (который может быть проверен провайдером услуг) и перенаправляет пользователя на URL, указанный в параметре запроса redirect, включая публичный токен как параметр запроса в перенаправлении (authToken).
      • Если неудачно, пользователь перенаправляется обратно на страницу входа.

Пример успешного ответа на пример URL: https://img.domain.com/getdata?authToken={jwt}, где jwt – это сгенерированный публичный токен. Провайдеры услуг могут проверить этот токен, потому что он сгенерирован с помощью приватного ключа, публичный ключ которого известен провайдеру услуг.

Используемые библиотеки

  • express – для запуска сервера.
  • cookie-parser – для установки и чтения (подписанных) cookies.
  • jsonwebtoken – для генерации и проверки JWT (с парой ключей RSA).

Поток аутентификации

Поток, который я пытаюсь реализовать, следующий, для этого примера я буду предполагать, что cookie еще не установлена у провайдера услуг:

  1. Пользователь отправляет запрос к аутентифицированному пути, например, https://img.domain.com/getdata.
  2. Провайдер услуг проверяет, установлена ли cookie Authorization, или установлен ли параметр запроса authToken.
  3. Поскольку ни cookie, ни параметр запроса не установлены, он перенаправляет пользователя к провайдеру идентификации, который показывает страницу входа, например, https://auth.domain.com/?redirect=https://img.domain.com/getdata.
  4. Пользователь входить в систему с использованием своих учетных данных, служба идентификации создает приватный токен и публичный токен и перенаправляет на оригинальный URL вместе с публичным токеном в параметре запроса authToken, например, https://img.domain.com/getdata?authToken={jwt}.
  5. Провайдер услуг проверяет параметр запроса authToken, валидирует его и сохраняет как httpOnly cookie Authorization.
  6. Затем провайдер услуг перенаправляет на свой собственный путь без параметра запроса (чтобы очистить его), например, https://img.domain.com/getdata.
  7. Теперь провайдер услуг должен видеть cookie Authorization и использовать его для проверки пользователя.

Код провайдера услуг следующий (упрощенный):

server.get('/getdata', authenticate, (req, res) => { res.send("крутые вещи") });

function authenticate(req, res, next) {
    if (req.query.authToken && validateToken(req.query.authToken)) {
        // Если действительный, то установить его как cookie.
        res.cookie("Authorization", req.query.authToken, { httpOnly: true, signed: true });
        // Очистить параметр запроса и перенаправить.
        res.redirect( req.originalUrl.split("?").shift() );
        return;
    } else if (req.signedCookies.Authorization && validateToken(query.signedCookies.Authorization) {
        next();
        return;
    }

    // Действительный токен не найден, поэтому перенаправить к службе идентификации.
    res.redirect(`auth.domain.com/?redirect=https://img.domain.com/${req.originalUrl}`)
}

function validateToken(token) {
    // Делает что-то для проверки токена.
}

Проблема

Поток от провайдера услуг к провайдеру идентификации и обратно проходит корректно (шаги 1 – 4), но сохранение cookie Authorization не работает.

Параметр запроса authToken правильно извлекается, и его валидация проходит успешно, но как только параметры запроса очищаются и снова выполняется перенаправление, cookie Authorization не видна (я также проверил req.cookies, который не содержит его).

Я изучал проблему и видел разные мнения о том, как cookies точно устанавливаются в HTTP, но так и не смог точно понять, почему это не работает. Кто-нибудь знает почему? Я также открыт для обратной связи по моему потоку аутентификации, возможно, это просто не правильный способ сделать это.

Должен добавить, что я пробовал это только при запуске img-server локально (http://localhost:3000). auth-server размещен на HTTPS.

Спасибо заранее!

Ответ или решение

Важным моментом для обеспечения безопасности и удобства использования является правильное управление куками в процессе аутентификации. В данном случае у нас есть два приложения: auth-server (идентификатор) и img-server (провайдер услуг), и возникает задача корректного хранения httpOnly cookie после перенаправления.

Контекст и описание проблемы

Вы описали аутентификационный процесс между двумя серверами, который включает в себя получение токенов из куки и редиректы. Проблема состоит в том, что после установки нового cookie Authorization, оно не доступно при следующем запросе, что мешает корректной аутентификации пользователя.

Почему это происходит? В HTTP своим поведением редиректы создают новый запрос, что означает, что куки, установленные на одном этапе (при помощи метода res.cookie), могут не быть включены в нового запроса, если он происходит на локальном сервере и имеет неправильные настройки.

Решения проблемы

Вот несколько моментов, на которые стоит обратить внимание:

  1. Проблема с доменами и путями:
    Убедитесь, что куки устанавливаются для правильного домена и пути. Если вы работаете с localhost, возможно, стоит добавить явное указание на домен. Например:

    res.cookie("Authorization", req.query.authToken, { 
       httpOnly: true, 
       signed: true,
       domain: 'img.domain.com',  // Убедитесь, что здесь указан правильный домен
       path: '/'  // Задайте путь, если это необходимо
    });
  2. Настройки безопасности:
    В вашем случае auth-server работает через HTTPS, а img-server — локально через HTTP. Это может повлиять на возможность передачи куки между серверами. С включённой настройкой secure: true куки будут передаваться только по защищенному соединению (HTTPS). Чтобы это исправить в тестовой среде, можно временно убрать это ограничение, хотя это не рекомендуется для производственной среды.

    res.cookie("Authorization", req.query.authToken, { 
       httpOnly: true, 
       signed: true,
       secure: process.env.NODE_ENV === 'production'  // Устанавливайте по условию окружения
    });
  3. Правильная обработка запросов:
    В своем коде убедитесь, что вы обрабатываете куки правильно и проверяете их наличие в дальнейшем коде. Например:

    else if (req.signedCookies.Authorization && validateToken(req.signedCookies.Authorization)) {
       next();
       return;
    }
  4. Тестирование на производственном окружении:
    Так как вы тестируете локально, вы можете столкнуться с дополнительными ограничениями, которые не будут актуальны на реальном сервере. Возможно, стоит сделать тест на тестовой среде, которая близка к производственной.

Потенциальные улучшения потока аутентификации

  1. Используйте сингл-серверную архитектуру: Если возможно, рассмотрите возможность объединения аутентификации и обслуживания в одном приложении, чтобы избежать проблем с куками между доменами.

  2. JWT как токен доступа: Рассмотрите использование JWT как способ облегчения аутентификации. Вместо передачи токена как куки, в заголовке запроса может передаваться Authorization: Bearer token, что упрощает его валидацию на всех уровнях.

  3. Используйте одни и те же домены: Если возможно, управлять аутентификацией в рамках одного домена (например, через поддомены). Это упростит управление куками.

Заключение: Важно правильно настроить куки, особенно в распределенных системах аутентификации. Следуя представленным рекомендациям, вы можете устранить проблемы с доступностью ваших httpOnly cookie после редиректов и гарантировать безопасность данных ваших пользователей.

Оцените материал
Добавить комментарий

Капча загружается...