При работе с подписью JWT в библиотеке jose критически важно понимать различие между обязательными и опциональными параметрами, поскольку именно они определяют корректность криптографической операции и совместимость токена с другими системами.
Без криптографического ключа операция подписи невозможна. В jose ключ передаётся в виде одного из поддерживаемых типов:
KeyObject (например, из crypto в
Node.js)Uint8Array для симметричных алгоритмов (HS256,
HS512)Ключ должен соответствовать выбранному алгоритму. Например:
Несоответствие ключа алгоритму приводит к ошибкам на этапе выполнения или к созданию некорректной подписи.
Алгоритм задаётся в защищённом заголовке JWT и является обязательным параметром:
setProtectedHeader({ alg: 'RS256' })
Алгоритм определяет:
Без alg библиотека не может сформировать корректный JWS,
так как структура подписи становится неопределённой.
Payload — это данные, которые будут подписаны. В jose он может быть:
Пример структуры:
new SignJWT({ sub: '123', role: 'admin' })
Payload участвует в формировании подписи, поэтому любые изменения после подписания делают токен недействительным.
sign(key)Финальный обязательный этап — вызов метода:
await jwt.sign(privateKey)
На этом этапе происходит:
Без передачи ключа операция невозможна.
При использовании SignJWT существует набор методов,
которые формируют структуру токена. Некоторые из них фактически
обязательны в реальных сценариях, несмотря на то, что технически не
всегда требуют явного вызова.
.setProtectedHeader({ alg: 'ES256' })
Этот вызов обязателен, так как именно он определяет алгоритм подписи. Без него токен не может быть корректно подписан.
Заголовок JWT может содержать дополнительные поля, которые не влияют напрямую на алгоритм, но расширяют возможности взаимодействия.
Используется для выбора ключа на стороне верификации:
.setProtectedHeader({
alg: 'RS256',
kid: 'key-2024-01'
})
Полезен при ротации ключей и работе с несколькими ключами одновременно.
Определяет тип токена:
{ typ: 'JWT' }
Чаще всего используется для явного указания формата, но не является обязательным.
Используется крайне редко. Позволяет указать обязательные расширенные поля заголовка, которые должны быть распознаны получателем.
Применяется в инфраструктуре с сертификатами:
Не является обязательным и используется только в специфических корпоративных сценариях.
Хотя payload обязателен как структура, его содержимое в JWT часто полностью опционально с точки зрения спецификации.
Определяет издателя токена.
Идентификатор субъекта.
Получатель токена.
Время истечения.
Время начала действия.
Время выпуска.
Уникальный идентификатор токена.
В jose эти поля часто задаются через специализированные методы:
.setIssuedAt()
.setExpirationTime('2h')
.setNotBefore('0s')
.setJti('unique-id')
Их использование не является строго обязательным с точки зрения криптографии, но критично для безопасности системы.
Помимо явно передаваемых параметров существуют неочевидные требования, которые фактически становятся обязательными:
Ошибка возникает при попытке:
Payload должен быть сериализуемым в JSON (если это объект). Нельзя использовать:
jose автоматически использует base64url, но любые нестандартные входные данные могут привести к несовместимости с другими JWT-реализациями.
В некоторых режимах JWS допускает наличие незашищённых заголовков, но это используется редко и снижает безопасность.
В JWS возможно создание detached payload, где данные не включаются в сам токен:
В jose важно понимать, что система разделяет:
Отсутствие первых приводит к невозможности подписи, отсутствие вторых — к снижению управляемости и безопасности системы, а отсутствие третьих обычно влияет только на логику приложения, но не на криптографическую корректность.