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

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

На мой взгляд, реализовать стратегию аутентификации JWT несложно, за исключением одной сложной части — стратегии токена обновления. Поскольку веб-токен JSON имеет срок действия, у клиента должен быть механизм для запроса нового токена после того, как запрос вернет статус 401 «Неавторизованный». По сути это будет работать так:

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

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

В этой статье я покажу, как реализовать эту стратегию с помощью Angular и NgRx, и поделюсь репозиторием шаблонов GitHub, который включает в себя проект с JWT-аутентификацией, реализованный в бэкэнде NestJs, вместе с настройкой Nx Monorepo, готовой для повышения вашего нового полноценный проект.

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

Во-первых, нужно определить форму состояния авторизации, и это просто, это будут просто два строковых поля, подобные этому:

interface AuthInfo {
  accessToken: string;
  refreshToken: string;
}

Затем нам нужно будет использовать функцию Angular HTTP Interceptors, чтобы поймать ошибку запроса 401, вы должны принять во внимание, что следующий код — это способ, которым я бы его реализовал, но если у вас есть другое мнение, не стесняйтесь, дайте мне знать.

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

function refreshTokenInterceptor(req, next) {
  return next(req).pipe(
    catchError((err) => {
      if (err.status === 401) {
        // refresh the token
        return of(store.dispatch(AuthActions.refreshTokenRequest())).pipe(

          switchMap(() => store.select(AuthFeature.selectAuthInfo)),

          // only emits when the store has the new accessToken
          first(
            (info) =>
              info.accessToken !==
              req.headers.get("authorization")?.split(" ")[1]
          ),

          switchMap((info) => {
            // clone the request and return a new observable to the switch map operator
            return next(
              req.clone({
                headers: req.headers.set(
                  "Authorization",
                  `Bearer ${info.accessToken}`
                ),
              })
            );
          })
        );
      }
      return of(err)
    })
  )
}

Взглянув на вкладку сети браузера, вы заметите, что стратегия токена обновления работает должным образом.

Надеюсь, эта статья окажется полезной, как и репозиторий шаблонов GitHub.

До следующего ✌🏻.