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

Аутентификация на основе сеанса

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

Вот процесс проверки подлинности на основе сеанса.

  • клиент — — входит в систему с именем пользователя, паролем — — → сервер
  • сервер хранит данные сеанса
  • клиент ‹ — — отправляет куки клиенту (идентификатор сеанса: abc) — — сервер
  • клиент — — отправляет запрос аутентификации, такой как GET (идентификатор сеанса: abc в файле cookie) — → сервер
  • сервер сравнивает идентификатор сеанса с данными, хранящимися в его памяти/БД
  • клиент ‹ — — отправляет ответ на каждый последующий запрос — — сервер

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

Аутентификация на основе токенов

Типичным выбором для аутентификации на основе токенов является использование JWT (веб-токены JSON). В этом методе токен не хранится на стороне сервера, а хранится на стороне клиента в localStorage (в основном). Клиент отправляет дополнительные запросы вместе с токеном, и сервер проверяет токен.

Вот процесс аутентификации JWT.

  • клиент — — входит в систему с именем пользователя, паролем — → сервер
  • сервер создает зашифрованный JWT для пользователя
  • клиент ‹ — — отправляет в зашифрованном виде клиенту — —серверу
  • клиент — — отправляет запросы аутентификации с JWT в заголовке — → сервер
  • сервер сравнивает JWT и проверяет, действителен ли он
  • клиент ‹ — — отправляет ответ на каждый последующий запрос — — сервер

Запрос с токеном в заголовке, отправляемый на сервер, выглядит так:

{  
  method: "GET",  
  headers: {   
    "Authorization": "Bearer ${JWT_TOKEN}"  
  } 
}

Обратите внимание, что значение токена начинается с Bearer, сервер должен удалить этот префикс при проверке значения токена.

Клиент несет ответственность за поддержание и удаление токена в нужное время, это более простой и легкий подход, учитывая, что не требуется хранилище на стороне сервера, однако иногда возникает проблема скомпрометированного секретного ключа. Также следует избегать включения конфиденциальной информации в JWT, поскольку ее можно легко зашифровать.

Токен обновления против токена доступа

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

Маркеры доступа несут необходимую информацию для прямого доступа к ресурсу. Другими словами, когда клиент передает маркер доступа серверу, управляющему ресурсом, этот сервер может использовать информацию, содержащуюся в маркере, чтобы решить, авторизован ли клиент или нет. Токены доступа обычно имеют срок действия и недолговечны.

Жетоны обновления несут информацию, необходимую для получения нового токена доступа. Другими словами, всякий раз, когда для доступа к определенному ресурсу требуется токен доступа, клиент может использовать токен обновления, чтобы получить новый токен доступа, выданный сервером аутентификации. Общие варианты использования включают получение новых токенов доступа после истечения срока действия старых или получение доступа к новому ресурсу в первый раз. Жетоны обновления также могут истечь, но они довольно долговечны. К токенам обновления обычно предъявляются строгие требования к хранению, чтобы предотвратить их утечку. Они также могут быть занесены в черный список сервером авторизации.

Обратите внимание, что в потоке есть 3 роли, включающие токены обновления и токены доступа.

  • Клиент: запрашивает ресурсы у сервера ресурсов, запрашивает у сервера аутентификации новый токен доступа.
  • Сервер аутентификации: получает токен обновления от клиента, отправляет обратно новый токен доступа.
  • Сервер ресурсов: получает запрос с токеном доступа от клиента, проверяет его и отправляет обратно защищенные ресурсы.

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

type User { ... }  
export const createRefreshToken = (user: User) => {   
  return sign(
    { userId: user.id }, 
    process.env.REFRESH_TOKEN_SECRET!, 
    { expiresIn: '7d' }
  ); 
};
export const createAccessToken = (user: User) => {   
  return sign(
    { userId: user.id }, 
    process.env.ACCESS_TOKEN_SECRET!, 
    { expiresIn: '15m' }
  ); 
};

На следующем этапе давайте поговорим о соображениях безопасности и масштабируемости этих двух сценариев, а также о том, как и когда следует выбирать один из них.