Компактная сериализация JSON Web Signature (JWS) представляет подпись в виде одной строки, разделённой точками:
BASE64URL(Protected Header) . BASE64URL(Payload) . BASE64URL(Signature)
Каждая часть строго определена:
Пример:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWV9.
MEUCIQD...
Используется кодирование Base64URL, отличающееся от
стандартного Base64 отсутствием символов +, /
и =.
Библиотека jose реализует стандарты JOSE (JSON Object Signing and Encryption), включая JWS, JWE, JWT. Основные особенности:
Установка:
npm install jose
Минимальный пример создания JWS с использованием алгоритма ES256:
import { SignJWT, generateKeyPair } from 'jose'
// генерация ключевой пары
const { privateKey } = await generateKeyPair('ES256')
// создание токена
const jwt = await new SignJWT({ user: 'admin' })
.setProtectedHeader({ alg: 'ES256' })
.setIssuedAt()
.setExpirationTime('2h')
.sign(privateKey)
console.log(jwt)
Для полного контроля используется класс CompactSign:
import { CompactSign } from 'jose'
const encoder = new TextEncoder()
const payload = encoder.encode(JSON.stringify({ data: 'test' }))
const jws = await new CompactSign(payload)
.setProtectedHeader({ alg: 'HS256' })
.sign(secretKey)
Особенности:
Для валидации применяется jwtVerify или
compactVerify.
import { jwtVerify } from 'jose'
const { payload, protectedHeader } = await jwtVerify(jwt, publicKey)
console.log(payload)
console.log(protectedHeader)
import { compactVerify } from 'jose'
const { payload, protectedHeader } = await compactVerify(jws, key)
const decoded = new TextDecoder().decode(payload)
console.log(JSON.parse(decoded))
Наиболее часто используемые поля:
JWT)Пример:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-1"
}
Поддерживаемые категории:
import { createSecretKey } from 'crypto'
const key = createSecretKey(Buffer.from('supersecret'))
import { generateKeyPair } from 'jose'
const { publicKey, privateKey } = await generateKeyPair('RS256')
Пример ручного кодирования:
function base64url(input) {
return Buffer.from(input)
.toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
}
В библиотеке jose это выполняется автоматически.
Проверка подписи — только часть процесса. Также необходимо учитывать:
exp)iat)aud)iss)Пример:
await jwtVerify(token, key, {
issuer: 'https://auth.example.com',
audience: 'my-app'
})
Поддерживаются различные форматы:
Пример импорта JWK:
import { importJWK } from 'jose'
const jwk = {
kty: 'oct',
k: 'hJtXIZ2uSN5kbQfbtTNWbg'
}
const key = await importJWK(jwk, 'HS256')
JWS поддерживает режим, где payload не включён в строку:
BASE64URL(header) .. BASE64URL(signature)
Используется для подписания внешних данных (например, HTTP-тел).
Критические моменты:
nonealgПример ограничения алгоритмов:
await jwtVerify(token, key, {
algorithms: ['ES256']
})
import {
generateKeyPair,
SignJWT,
jwtVerify
} from 'jose'
// генерация ключей
const { publicKey, privateKey } = await generateKeyPair('ES256')
// подпись
const token = await new SignJWT({ role: 'user' })
.setProtectedHeader({ alg: 'ES256' })
.setIssuedAt()
.setExpirationTime('1h')
.sign(privateKey)
// проверка
const { payload } = await jwtVerify(token, publicKey)
console.log(payload)
| Характеристика | Compact JWS | JSON JWS |
|---|---|---|
| Формат | строка | JSON |
| Поддержка нескольких подписей | нет | да |
| Размер | компактный | больше |
| Удобство передачи | высокий | средний |
Компактная сериализация применяется в большинстве случаев, особенно в JWT.
Компактный формат удобен для передачи в HTTP-заголовках:
Authorization: Bearer <token>
Для анализа токена:
const [header, payload, signature] = token.split('.')
console.log(JSON.parse(Buffer.from(header, 'base64url')))
console.log(JSON.parse(Buffer.from(payload, 'base64url')))
Библиотека jose соответствует:
Поддерживается:
Компактная сериализация JWS остаётся базовым форматом для безопасной передачи подписанных данных в современных веб-приложениях.