Токены обновления — важный аспект защиты современных веб-приложений. Они позволяют пользователю оставаться аутентифицированным даже после истечения срока действия его первоначального токена доступа. В этом руководстве мы обсудим, как реализовать токены обновления с помощью Axios, популярной библиотеки JavaScript для выполнения HTTP-запросов.

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

  1. Пользователь входит в приложение и получает маркер начального доступа и маркер обновления.
  2. Пользователь делает запрос к серверу, используя токен доступа.
  3. Если срок действия токена доступа истек, сервер вернет ошибку 401 Unauthorized.
  4. Затем клиент использует токен обновления для запроса нового токена доступа с сервера.
  5. Сервер проверяет токен обновления и, если он действителен, возвращает новый токен доступа.

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

Во-первых, нам нужно создать конечную точку на сервере, которая принимает токен обновления и возвращает новый токен доступа. Эта конечная точка должна быть защищена и доступна только для аутентифицированных пользователей. Вот пример того, как эта конечная точка может выглядеть с помощью Express.js:

app.post("/refresh_token", (req, res) => {
  const refreshToken = req.body.refreshToken;

  // Verify the refresh token
  if (refreshToken === validRefreshToken) {
    // Generate a new access token
    const accessToken = jwt.sign({ userId: user.id }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "1h" });
    res.json({ accessToken });
  } else {
    res.status(401).json({ message: "Invalid refresh token" });
  }
});

Затем нам нужно обработать ошибку 401 Unauthorized на стороне клиента и использовать токен обновления для запроса нового токена доступа. Мы можем использовать библиотеку Axios, чтобы сделать этот запрос. Вот пример того, как это сделать:

axios.interceptors.response.use(
  response => response,
  error => {
    const originalRequest = error.config;

    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      const refreshToken = localStorage.getItem("refreshToken");

      return axios.post("/refresh_token", { refreshToken })
        .then(res => {
          localStorage.setItem("accessToken", res.data.accessToken);
          axios.defaults.headers.common["Authorization"] = "Bearer " + res.data.accessToken;
          originalRequest.headers["Authorization"] = "Bearer " + res.data.accessToken;

          return axios(originalRequest);
        });
    }

    return Promise.reject(error);
  }
);

В этом примере мы используем перехватчик Axios для прослушивания ответов. Если сервер возвращает ошибку 401 Unauthorized, мы знаем, что срок действия токена доступа истек. Затем мы используем токен обновления, чтобы запросить новый токен доступа с сервера. Как только мы получаем новый токен доступа, мы обновляем локальное хранилище и заголовки исходного запроса, а затем повторяем исходный запрос с новым токеном доступа.

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

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

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

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

Реализация на стороне сервера с использованием Node.JS

  1. Во-первых, нам нужно будет установить необходимые пакеты:
npm install express jsonwebtoken

2. Далее мы создадим новое приложение Express.js и настроим маршрут для конечной точки токена обновления:

const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();

app.use(express.json());

app.post("/refresh_token", (req, res) => {
  const refreshToken = req.body.refreshToken;
  // Verify the refresh token
  try {
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
    // Generate a new access token
    const accessToken = jwt.sign({ userId: decoded.userId }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "1h" });
    res.json({ accessToken });
  } catch (err) {
    res.status(401).json({ message: "Invalid refresh token" });
  }
});

app.listen(3000, () => console.log("Server started on port 3000"));

В этом примере мы используем библиотеку jsonwebtoken для проверки токена обновления и создания нового токена доступа. Функция verify используется для проверки правильности токена обновления и выдает ошибку, если это не так. Мы также устанавливаем время истечения токена доступа на 1 час.

Вам также потребуется установить переменную среды REFRESH_TOKEN_SECRET и ACCESS_TOKEN_SECRET с секретным ключом для подписи и проверки токенов.

3. Далее нам нужно обработать случай, когда пользователь выходит из приложения. В этом случае мы должны аннулировать токен обновления на сервере. Один из способов сделать это — хранить токены обновления в базе данных с флагом revoked и проверять этот флаг при отправке токена обновления на сервер. Вот пример того, как это сделать:

const db = require("./db"); // assuming you have a database connection

app.post("/logout", (req, res) => {
  const refreshToken = req.body.refreshToken;

  // Mark the refresh token as revoked in the database
  db.query("UPDATE refresh_tokens SET revoked = true WHERE token = ?", [refreshToken], (err) => {
    if (err) throw err;
    res.json({ message: "Refresh token revoked" });
  });
});

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

Это базовый пример реализации токенов обновления с помощью Node.js и Express.js. Имейте в виду, что существует множество способов реализации токенов обновления, и вы должны использовать метод, который лучше всего соответствует потребностям безопасности вашего приложения.