Pular para o conteúdo
ecdsa.com
PHP

web-token (JWT Framework)

O JWT Framework (web-token) de Florent Morselli, a implementação completa de JOSE para PHP e Symfony: um AlgorithmManager que é a lista de algoritmos permitidos, verificadores de claims separados, todos os algoritmos de assinatura da RFC 7518 e também EdDSA — mas sem ES256K.
Licença
MIT
Versão verificada
4.2.3 ·
Código-fonte
github.com/web-token/jwt-framework
Documentação
web-token.spomky-labs.com/

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

O web-token implementa JWS, JWE, JWK e conjuntos de JWK como componentes separados; o web-token/jwt-library reúne todos eles, e um bundle do Symfony os registra como serviços. É a opção em PHP quando você precisa de JWE, de gerenciamento de conjuntos de JWK ou da lista completa de algoritmos: a versão 4.2.3 assinou e verificou HS, RS, PS e ES em 256/384/512 e EdDSA, incluindo PS384/512 e ES512, que faltam no firebase/php-jwt. Não há classe de algoritmo para ES256K nem para o Ed25519 totalmente especificado.

Os algoritmos são permitidos explicitamente: o AlgorithmManager passado ao JWSVerifier é a lista de algoritmos permitidos, e um token em qualquer outro algoritmo é rejeitado com The algorithm "RS256" is not supported. O none existe como classe de algoritmo, mas só é aceito se você o incluir no gerenciador. Um token HS256 forjado com o texto PEM da chave EC falhou mesmo com o HS256 permitido, porque uma JWK EC não pode ser usada como chave HMAC. O ES256 com uma chave P-384 produziu uma assinatura que o próprio verificador da biblioteca rejeitou.

A verificação da assinatura e a validação das claims são etapas separadas: JWSVerifier::verifyWithKey() retornou true para um token que tinha expirado uma hora antes. Rode um ClaimCheckerManager em seguida, liste as claims obrigatórias — sem isso, a exp não é obrigatória — e passe uma tolerância ao ExpirationTimeChecker, que desde a versão 4 também exige um relógio PSR-20; o exemplo mostra a sequência completa.

As assinaturas ES256 são 64 bytes de r‖s brutos, e uma assinatura DER faz verifyWithKey() retornar false; o depurador de JWT mostra qual das duas um token traz. As chaves são sempre objetos JWK; a JWKFactory as cria a partir de PEM, de certificados ou de parâmetros de chave, e o construtor de JWKS gera conjuntos de chaves no mesmo formato.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/web-token-jwt-framework/run.sh executou web-token (JWT Framework) 4.2.3 na imagem de contêiner php:8.5.11-cli-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Assinatura e verificação funcionaram com HS, RS, PS e ES em 256/384/512 e com EdDSA; não há classes de algoritmo para ES256K nem para Ed25519.

    Registrado pela verificação
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: class Jose\Component\Signature\Algorithm\ES256K exists: false
    evidence jws-algorithms: Ed25519: class Jose\Component\Signature\Algorithm\Ed25519 exists: false
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER faz verifyWithKey() retornar false.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: false
  • Lista de algoritmos permitidos

    required

    O AlgorithmManager é a lista de algoritmos permitidos: um token RS256 verificado com um gerenciador que só tinha ES256 foi rejeitado com The algorithm "RS256" is not supported.

    Registrado pela verificação
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token with an AlgorithmManager holding only ES256: InvalidArgumentException: The algorithm "RS256" is not supported.
  • Tokens sem assinatura (alg none)

    opt-in

    Um token sem assinatura é rejeitado com The algorithm "none" is not supported., a menos que a classe de algoritmo None seja adicionada ao gerenciador.

    Registrado pela verificação
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with an ES256 AlgorithmManager: InvalidArgumentException: The algorithm "none" is not supported.; a None algorithm class exists: true
  • Chave pública usada como segredo HMAC

    rejected

    Com ES256 e HS256 permitidos, um token HS256 que usava o texto PEM como chave falhou na verificação com a JWK EC.

    Registrado pela verificação
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: AlgorithmManager [ES256, HS256] with the EC JWK: false
  • Curva conferida com o alg

    enforced

    A assinatura ES256 com uma chave P-384 funcionou, mas o verificador da biblioteca rejeitou o resultado.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key, signing: ok; verifying: false
  • Claims validadas na decodificação

    no

    JWSVerifier::verifyWithKey() retornou true para um token expirado uma hora antes; as claims são verificadas por um ClaimCheckerManager separado.

    Registrado pela verificação
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: JWSVerifier on a token expired an hour ago: ok
  • Tolerância padrão de diferença de relógio

    0s

    O ExpirationTimeChecker não admite nenhuma tolerância, a menos que você informe uma: um token expirado três segundos antes foi rejeitado.

    Registrado pela verificação
    fact jwt-clock-skew = 0s
  • Claim de expiração obrigatória

    no

    Sem exp entre as claims obrigatórias, um token sem essa claim passou; incluí-la na lista fez a biblioteca lançar MissingMandatoryClaimException.

    Registrado pela verificação
    fact jwt-exp-required = no
    evidence jwt-exp-required: ExpirationTimeChecker without exp: ok; with exp listed as mandatory: Jose\Component\Checker\MissingMandatoryClaimException: The following claims are mandatory: exp.

Uso

Assine com ES256 usando a lista de algoritmos permitidos de um AlgorithmManager, verifique a assinatura e depois valide exp e iss com verificadores de claims.
<?php
require 'vendor/autoload.php';

use Jose\Component\Checker\{ClaimCheckerManager, ExpirationTimeChecker, IssuerChecker};
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\{JWSBuilder, JWSVerifier};
use Jose\Component\Signature\Serializer\CompactSerializer;

$jwk = JWKFactory::createECKey('P-256');
$algorithms = new AlgorithmManager([new ES256()]); // the allow-list
$payload = json_encode(['sub' => 'user-42', 'iss' => 'https://auth.example.com', 'exp' => time() + 600]);
$jws = (new JWSBuilder($algorithms))->create()->withPayload($payload)->addSignature($jwk, ['alg' => 'ES256'])->build();
$token = (new CompactSerializer())->serialize($jws, 0);

// The signature check does not look at claims: run the claim checkers as a second step.
$parsed = (new CompactSerializer())->unserialize($token);
$valid = (new JWSVerifier($algorithms))->verifyWithKey($parsed, $jwk->toPublic(), 0);
$clock = new class implements Psr\Clock\ClockInterface { public function now(): DateTimeImmutable { return new DateTimeImmutable(); } };
$claims = json_decode($parsed->getPayload(), true);
(new ClaimCheckerManager([new ExpirationTimeChecker($clock, 30), new IssuerChecker(['https://auth.example.com'])]))->check($claims, ['exp', 'iss']);
echo 'valid: ', var_export($valid, true), ', sub: ', $claims['sub'], "\n";

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner php:8.5.11-cli-trixie, e a execução falha se a saída não corresponder.

Configuração
curl -fsSL -o /tmp/composer.phar https://getcomposer.org/download/2.10.3/composer.phar && echo '7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6 /tmp/composer.phar' | sha256sum -c - >/dev/null && apt-get -qq update >/dev/null && apt-get -qq install -y unzip >/dev/null 2>&1 && COMPOSER_ALLOW_SUPERUSER=1 php /tmp/composer.phar require --quiet web-token/jwt-library:4.2.3
Comando
php usage.php
A saída deve corresponder a (expressão regular)
valid: true, sub: user-42

Bibliotecas