Пакет · jwt

Работа с claims

Claims (утверждения) — это полезная нагрузка токена, те заявления, которые вы подписываете. Это руководство охватывает стандартные (зарегистрированные) claims, которые библиотека проверяет за вас, как добавлять собственные и как безопасно читать их обратно.

Установка claims

JwtPayload — это обычный ассоциативный массив claims. Стандартные (зарегистрированные) claims используют короткие трёхбуквенные ключи; вы можете добавить к ним любые собственные ключи по своему усмотрению:

php
use Flytachi\Jwt\Entity\JwtPayload;

$now = time();
$payload = new JwtPayload([
  // стандартные (зарегистрированные) claims
  'iss' => 'https://my-app.com',       // издатель
  'sub' => 'user-42',                  // субъект (о ком токен)
  'aud' => 'https://api.my-app.com',   // аудитория
  'iat' => $now,                       // время выпуска
  'nbf' => $now,                       // недействителен ранее
  'exp' => $now + 3600,                // истекает через один час
  // пользовательские claims — всё, что сериализуется в JSON
  'role'   => 'admin',
  'scopes' => ['orders:read', 'orders:write'],
]);

Временные claims проверяются автоматически

Три зарегистрированных claim управляют окном валидности токена, и decode() проверяет за вас все три:

Claim Значение Отклоняется, когда
exp Время истечения текущее время (минус допуск) достигло или превысило exp
nbf Недействителен ранее nbf позже текущего времени (плюс допуск)
iat Время выпуска iat позже текущего времени (плюс допуск)

Каждый из них необязателен — опустите exp, и токен никогда не истечёт (редко то, что вам нужно). Все значения — это Unix-время (timestamp) в секундах.

Всегда устанавливайте exp

Токен без exp действителен вечно, поэтому утёкший токен нельзя вывести из обращения по времени. Задайте короткий срок жизни (от минут до часа для токенов доступа) и выпускайте новый токен, когда прежний истекает.

Учитывайте рассинхронизацию часов с помощью допуска (leeway)

Часы серверов расходятся на несколько секунд. decode() принимает третий аргумент, leeway (в секундах), который расширяет окно приёма при каждой проверке времени — так что токен, который только что истёк или чей nbf на секунду в вашем будущем, не будет отклонён из-за крошечного расхождения:

php
use Flytachi\Jwt\JWT;

// Допускать до 60 секунд разницы в часах
$payload = JWT::decode($token, [$publicKey], leeway: 60);

Держите leeway малым — десятки секунд. Большие значения фактически продлевают жизнь каждого токена сверх его заявленного exp.

Чтение claims обратно

getClaim() читает один claim с необязательным значением по умолчанию, если он отсутствует:

php
$userId = $payload->getClaim('sub');              // 'user-42'
$role   = $payload->getClaim('role', 'guest');    // значение по умолчанию, если отсутствует
$scopes = $payload->getClaim('scopes', []);       // ['orders:read', ...]

if (in_array('orders:write', $payload->getClaim('scopes', []), true)) {
  // авторизован на запись заказов
}

Нужен весь набор сразу — для логирования или пересылки? Используйте toArray():

php
$all = $payload->toArray();
// ['iss' => 'https://my-app.com', 'sub' => 'user-42', 'role' => 'admin', ...]

getClaim никогда не бросает исключение на отсутствующем claim

Отсутствующий claim возвращает значение по умолчанию (null, если вы его не передали), поэтому чтение безопасно. Проверяйте claims, которые вам необходимы, явно — например, отклоняйте токен без sub.

Проверяйте прикладные claims самостоятельно

Помимо временных claims, смысл остаётся за вами. Проверяйте издателя, аудиторию и любой claim авторизации после декодирования:

php
use Flytachi\Jwt\JWTException;

$payload = JWT::decode($token, [$publicKey]);

if ($payload->getClaim('aud') !== 'https://api.my-app.com') {
  throw new JWTException('Token audience mismatch.');
}
if ($payload->getClaim('role') !== 'admin') {
  http_response_code(403);
  exit('Admins only.');
}

Связанное