JWT представляет собой компактный токен, состоящий из трёх частей, разделённых точками:
header.payload.signature
Каждая часть кодируется в формате Base64URL, а затем объединяется в строку, используемую для передачи и проверки подлинности данных.
Header (заголовок) содержит метаданные о токене и алгоритме подписи. Это JSON-объект, который после сериализации и кодирования становится первой частью JWT.
Типичный пример:
{
"alg": "HS256",
"typ": "JWT"
}
Ключевые поля header:
После формирования JSON он кодируется в Base64URL. Важно понимать, что header не защищён криптографически сам по себе — он лишь описывает, как должна проверяться подпись.
Payload содержит полезную нагрузку — данные, которые передаются внутри токена. Это также JSON-объект, который кодируется в Base64URL.
Пример payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1710000000
}
Типы claims (заявлений):
Registered claims — стандартные поля:
iss (issuer) — кто выпустил токенsub (subject) — идентификатор пользователяexp (expiration time) — время истеченияiat (issued at) — время выпускаaud (audience) — получатель токенаPublic claims — пользовательские, но стандартизируемые через namespace
Private claims — произвольные данные, определённые приложением
Payload не шифруется, а лишь кодируется. Это означает, что его содержимое можно прочитать, но нельзя изменить без нарушения подписи.
Signature (подпись) обеспечивает целостность и подлинность JWT. Именно она гарантирует, что данные не были изменены после создания токена.
Формирование подписи происходит по следующему принципу:
signature = sign(
base64Url(header) + "." + base64Url(payload),
secret_or_private_key
)
При использовании симметричного алгоритма:
При использовании асимметричного алгоритма:
Библиотека jose в JavaScript предоставляет современный
API для работы с JWT и JOSE-стеком (JSON Web Signature, Encryption,
Keys).
Используется SignJWT:
import { SignJWT } from 'jose'
const secret = new TextEncoder().encode('secret-key')
const jwt = await new SignJWT({ userId: 123 })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('2h')
.sign(secret)
Здесь:
setProtectedHeader формирует headersign() создаёт signatureДля верификации используется jwtVerify:
import { jwtVerify } from 'jose'
const { payload, protectedHeader } = await jwtVerify(
token,
new TextEncoder().encode('secret-key')
)
Результат включает:
payload — декодированные данныеprotectedHeader — header токенаJWT строится строго по цепочке:
Любое изменение header или payload делает подпись недействительной, что позволяет обнаруживать подмену данных.
JWT использует модифицированную версию Base64:
+ заменяется на -/ заменяется на _=Это делает токен безопасным для использования в URL, заголовках HTTP и cookies.
В контексте jose чаще всего используются:
Выбор алгоритма влияет на структуру ключей и модель безопасности системы.
exp в payloadJWT как структура остаётся фиксированной: три части, каждая из которых выполняет строго определённую роль — описание алгоритма, данные и криптографическое подтверждение целостности.