JAR: подписанный запрос авторизации

JAR (JWT Secured Authorization Request) представляет собой механизм передачи параметров авторизационного запроса в виде подписанного JWT, что обеспечивает целостность, подлинность и защиту от подмены данных при взаимодействии клиента с авторизационным сервером в протоколах OAuth 2.0 и OpenID Connect.

Основная идея заключается в том, что вместо передачи набора query-параметров в URL или форме запроса, формируется JWT-объект, содержащий все параметры авторизации. Этот JWT подписывается криптографическим ключом клиента и передается в авторизационный сервер либо напрямую, либо через ссылку request_uri.

JAR использует стандарт JWS (JSON Web Signature), определённый в JOSE. Внутреннее содержимое токена включает стандартные OAuth-параметры:

  • client_id — идентификатор клиента
  • redirect_uri — URI возврата после авторизации
  • response_type — тип ответа (например, code)
  • scope — запрашиваемые разрешения
  • state — значение для защиты от CSRF
  • nonce — защита от replay-атак (в OpenID Connect)
  • дополнительные параметры расширений

JWT формируется в виде:

header.payload.signature

Где:

  • Header определяет алгоритм подписи (alg, kid)
  • Payload содержит параметры авторизационного запроса
  • Signature обеспечивает криптографическую защиту

Использование библиотеки jose для формирования JAR

Библиотека jose в JavaScript предоставляет инструменты для создания и проверки JWS, JWE и JWT. Для JAR используется именно JWS-подпись.

Создание подписанного авторизационного запроса:

import { SignJWT } from 'jose'
import { createPrivateKey } from 'crypto'

const privateKey = createPrivateKey(`
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
`)

const payload = {
  client_id: 'client_123',
  redirect_uri: 'https://client.example.com/callback',
  response_type: 'code',
  scope: 'openid profile email',
  state: 'af0ifjsldkj',
  nonce: 'n-0S6_WzA2Mj'
}

const jar = await new SignJWT(payload)
  .setProtectedHeader({ alg: 'RS256', typ: 'JWT' })
  .setIssuedAt()
  .setExpirationTime('5m')
  .sign(privateKey)

Полученный токен передаётся как параметр request или используется для формирования request_uri.

Использование request_uri

В ряде реализаций авторизационный сервер не принимает сам JWT напрямую, а требует ссылку на ресурс:

https://server.example.com/auth?request_uri=https://client.example.com/request.jwt

В этом случае JAR предварительно размещается на защищённом сервере клиента, откуда он загружается сервером авторизации.

Проверка JAR на стороне сервера авторизации

Авторизационный сервер выполняет валидацию подписи и содержимого JWT:

import { jwtVerify } from 'jose'
import { createPublicKey } from 'crypto'

const publicKey = createPublicKey(`
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
`)

const { payload, protectedHeader } = await jwtVerify(token, publicKey, {
  algorithms: ['RS256']
})

После проверки извлекаются параметры запроса и используются как обычный OAuth-запрос.

Связь JAR и PAR (Pushed Authorization Request)

JAR часто применяется совместно с механизмом PAR. В этом случае:

  • клиент формирует JAR
  • отправляет его на endpoint /par
  • получает request_uri
  • перенаправляет пользователя на authorization endpoint с этим request_uri

Такой подход исключает передачу чувствительных параметров через браузерный URL.

Криптографические особенности

JAR опирается на свойства JWS:

  • RSA (RS256, RS384, RS512)
  • ECDSA (ES256, ES384)
  • HMAC (HS256, HS512 — реже в публичных клиентах)

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

Формирование защищённого заголовка JWT

Заголовок JWS может включать дополнительные параметры:

.setProtectedHeader({
  alg: 'RS256',
  typ: 'JWT',
  kid: 'key-1'
})

Поле kid используется для выбора нужного публичного ключа при валидации на сервере.

Валидация параметров внутри payload

Помимо криптографической проверки, выполняется семантическая валидация:

  • соответствие redirect_uri зарегистрированным значениям клиента
  • проверка client_id
  • проверка срока действия exp
  • анализ scope на допустимость
  • контроль nonce и state на уникальность

Угрозы при отсутствии JAR

При передаче параметров авторизации через URL возникают следующие риски:

  • модификация query-параметров прокси-серверами
  • утечка параметров через referer-заголовки
  • подмена redirect_uri
  • атаки типа authorization request tampering

JAR устраняет эти проблемы за счёт криптографической фиксации состояния запроса.

Интеграция с современными OAuth-провайдерами

JAR поддерживается в расширениях:

  • OpenID Connect Advanced
  • FAPI (Financial-grade API)
  • OAuth 2.0 Security Best Current Practice

В таких системах JAR часто является обязательным требованием, особенно при работе с финансовыми данными.

Ошибки реализации

Часто встречающиеся проблемы:

  • использование симметричного алгоритма без необходимости
  • отсутствие проверки aud и iss
  • слишком длительный срок жизни JWT
  • генерация JAR на клиенте без защищённого хранения ключа
  • несоответствие redirect_uri при валидации

Особенности работы с библиотекой jose

Библиотека jose предоставляет низкоуровневый контроль над процессом формирования JWS, что требует явного управления:

  • выбор алгоритма подписи
  • управление ключами
  • настройка заголовков
  • контроль времени жизни токена

Это делает реализацию JAR гибкой, но требующей строгого соблюдения спецификации JOSE и OAuth 2.0.