«В предыдущей статье я объяснил, как можно реализовать авторизацию и аутентификацию через JWT с использованием симметричных алгоритмов, таких как HS256. Теперь я попытаюсь объяснить асимметричный подход (RS256).

Вы можете спросить: зачем нам нужен другой способ создания JWT? Что ж, этот ответ может помочь вам понять, почему.

Рабочий процесс использования JWT остается таким же, как и при симметричном подходе. Единственное отличие состоит в том, что теперь мы не используем secret для создания и проверки токена доступа. Вместо этого нам нужна пара открытых/закрытых ключей RSA. Мы используем закрытый ключ только для создания JWT, а открытый ключ — только для его проверки. Таким образом, вам не нужно хранить какую-то секретную информацию для проверки токенов. Вы можете сгенерировать такую ​​пару ключей, используя следующие команды:

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Итак, для генерации JWT мы делаем следующее

const encodedHeaderInBase64 = base64UrlEncodeJSON(header)
const encodedPayloadInBase64 = base64UrlEncodeJSON(payload)
const encodedSignatureInBase64 = generateSignature(`${encodedHeaderInBase64}.${encodedPayloadInBase64}`, privateKey)
const token = `${encodedHeaderInBase64}.${encodedPayloadInBase64}.${encodedSignatureInBase64}`

Где generateSignature в данном случае

const crypto = require('crypto')

function generateSignature (str, privateKey) {
  const sign = crypto.createSign('RSA-SHA256')
  sign.update(str)
  return sign.sign(privateKey, 'base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
}

И base64UrlEncodeJSON такой же.

function base64UrlEncodeJSON (json) {
  return Buffer.from(
    JSON.stringify(json)
  ).toString('base64')
   .replace(/\+/g, '-')
   .replace(/\//g, '_')
}

И для проверки мы можем использовать следующую функцию

// Returns true if token is valid, otherwise returns false
function isValid (token, secret) {
  const parts = token.split('.')
  const header = base64UrlDecodeToJSON(parts[0])
  const payload = base64UrlDecodeToJSON(parts[1])
  if (header.alg !== 'RS256' || header.typ !== 'JWT') {
    return false
  }
  const signature = parts[2]
  const exp = payload.exp
  if (exp) {
    if (exp < new Date().getTime()) {
      return false
    }
  }
  const verify = crypto.createVerify('RSA-SHA256')
  verify.update(unescape(`${parts[0]}.${parts[1]}`))
  return verify.verify(publicKey, signature, 'base64')
}

function base64UrlDecodeToJSON (str) {
  return JSON.parse(
    Buffer.from(
      unescape(str), 'base64'
    ).toString('utf8')
  )
}

function unescape (str) {
  return str.replace(/-/g, '+').replace(/_/g, '/')
}

Для тестирования вашего токена доступа вы также можете использовать эту услугу. Просто измените свойство alg на RS256.

Кроме того, я только что выпустил cutie-jwt. Итак, если вы хотите использовать JWT в декларативном стиле, вам обязательно стоит попробовать.

Рекомендации

Источник: https://guseyn.com/html/posts/simple-rs-jwt.html