Saltar al contenido
ecdsa.com
Rust

jsonwebtoken (Rust)

El crate jsonwebtoken, de Vincent Prouillet, la biblioteca JWT más usada de Rust: un enum Algorithm cerrado sin none, exp obligatorio, 60 segundos de tolerancia por defecto y, desde la versión 10, un backend criptográfico que tienes que elegir.
Licencia
MIT
Versión comprobada
11.1.0 ·
Código fuente
github.com/Keats/jsonwebtoken
Documentación
docs.rs/jsonwebtoken/11.1.0/jsonwebtoken/

Compatibilidad de un vistazo

JOSE y JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384EdDSA

jsonwebtoken codifica y decodifica JWT con encode, decode y un objeto Validation, y usa serde para los claims. Desde la versión 10, la criptografía viene de un backend que se elige con una característica (feature) de Cargo —aws_lc_rs o rust_crypto—, y la comprobación usó aws_lc_rs. Olvidar la característica no hace fallar la compilación: solo con las características predeterminadas, la 11.1.0 compiló y después entró en pánico en el primer encode() con Could not automatically determine the process-level CryptoProvider. Activa exactamente un backend, como hace el manifiesto del ejemplo.

El conjunto de algoritmos es un enum cerrado: con HS, RS y PS de 256/384/512, ES256, ES384 y EdDSA se firmó y se verificó, mientras que ES512, ES256K y Ed25519 ni siquiera son variantes. Tampoco existe la variante none, así que un token sin firmar falla al analizar su encabezado con unknown variant none. Validation::new(alg) es la lista de algoritmos permitidos: un token HS256 comprobado con Validation::new(ES256) falló con InvalidAlgorithm e, incluso con ambos algoritmos permitidos, un token HS256 con el texto PEM de la clave pública EC como clave falló con InvalidKeyFormat, porque una DecodingKey EC no puede actuar como secreto HMAC.

Las comprobaciones de los claims son más estrictas que en la mayoría de las bibliotecas: un token sin exp falla con Missing required claim: exp, y la tolerancia predeterminada es de 60 segundos, de modo que un token que había caducado 57 segundos antes aún se validó. Reduce leeway y fija el emisor y la audiencia en el Validation, como hace el ejemplo. ES256 rechaza una clave P-384 al firmar con InvalidEcdsaKey.

Las firmas ES256 son el r‖s en bruto de 64 bytes que exige JWS, y una firma DER falla con InvalidSignature. Las claves se cargan desde PEM (from_ec_pem, from_rsa_pem, from_ed_pem) y DER; para Ed25519 en DER, la clave de codificación aceptó PKCS#8 y la de decodificación, la clave pública en bruto de 32 bytes. Inspecciona los tokens con el decodificador de JWT y prueba los secretos HMAC con el comprobador de secretos JWT.

Para firmas fuera de JWT, usa directamente los crates de backend: aws-lc-rs o RustCrypto ecdsa.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/jsonwebtoken-rust/run.sh ejecutó jsonwebtoken (Rust) 11.1.0 en la imagen de contenedor rust:1.99.1-trixie el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384EdDSA

    Con HS, RS y PS de 256/384/512, ES256, ES384 y EdDSA se firmó y se verificó; ES512, ES256K y Ed25519 no son variantes de Algorithm.

    Registrado por la comprobación
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, EdDSA
    evidence jws-algorithms: ES512: not an Algorithm variant
    evidence jws-algorithms: ES256K: not an Algorithm variant
    evidence jws-algorithms: Ed25519: not an Algorithm variant
  • Codificación de la firma ES256

    raw

    Las firmas ES256 son 64 bytes de r‖s en bruto.

    Registrado por la comprobación
    fact jws-ecdsa-encoding = raw
  • Firma DER dentro de un JWS

    rejected

    Una firma ES256 codificada en DER falla con InvalidSignature.

    Registrado por la comprobación
    fact jws-der-signature = rejected
    evidence jws-der-signature: Err("InvalidSignature")
  • Lista de algoritmos permitidos

    required

    Validation::new(alg) fija los algoritmos permitidos; un token HS256 comprobado con Validation::new(ES256) falló con InvalidAlgorithm.

    Registrado por la comprobación
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: HS256 token with Validation::new(ES256): Err("InvalidAlgorithm")
  • Tokens sin firma (alg none)

    rejected

    none no es una variante de Algorithm: el análisis del encabezado falla con unknown variant none.

    Registrado por la comprobación
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: Err("JSON error: unknown variant `none`, expected one of `HS256`, `HS384`, `HS512`, `ES256`, `ES384`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `EdDSA` at line 1 column 13"); "none" parses as Algorithm: false
  • Clave pública usada como secreto HMAC

    rejected

    Con ES256 y HS256 permitidos, un token HS256 con el texto PEM como clave falló contra la DecodingKey EC con InvalidKeyFormat.

    Registrado por la comprobación
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: algorithms [ES256, HS256], EC DecodingKey: Err("InvalidKeyFormat")
  • Curva cotejada con alg

    enforced

    Firmar ES256 con una clave P-384 falla con InvalidEcdsaKey.

    Registrado por la comprobación
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: Ok(Err("InvalidEcdsaKey"))
  • Desfase de reloj predeterminado

    60s

    Validation::new fija leeway en 60 segundos: un token caducado 57 segundos antes se validó y uno caducado 63 segundos antes, no.

    Registrado por la comprobación
    fact jwt-clock-skew = 60s
    evidence jwt-clock-skew: Validation::new(HS256).leeway = 60
  • Claim de caducidad obligatorio

    yes

    Un token sin exp falla con Missing required claim: exp.

    Registrado por la comprobación
    fact jwt-exp-required = yes
    evidence jwt-exp-required: Err("Missing required claim: exp")
  • Sin backend criptográfico seleccionado

    runtime-panic

    Sin aws_lc_rs ni rust_crypto activados, el crate compiló y el primer encode() entró en pánico con Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features.

    Registrado por la comprobación
    fact crypto-provider-without-feature = runtime-panic
    evidence crypto-provider-without-feature: default features compile; the first encode() panics with: Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features.

Uso

Emite un token HS256 y valídalo con el algoritmo, el emisor y una tolerancia de 30 segundos fijados explícitamente.
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize)]
struct Claims {
    sub: String,
    iss: String,
    exp: u64,
}

fn main() {
    // Choose a backend explicitly: features = ["aws_lc_rs"] or ["rust_crypto"].
    let secret = b"use at least 32 random bytes from a secrets manager";
    let exp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 600;
    let claims = Claims { sub: "user-42".into(), iss: "https://auth.example.com".into(), exp };
    let token = encode(&Header::new(Algorithm::HS256), &claims, &EncodingKey::from_secret(secret)).unwrap();

    // Validation::new(alg) is the allow-list; exp is required, leeway defaults to 60 s.
    let mut validation = Validation::new(Algorithm::HS256);
    validation.set_issuer(&["https://auth.example.com"]);
    validation.leeway = 30;
    let data = decode::<Claims>(&token, &DecodingKey::from_secret(secret), &validation).unwrap();
    println!("sub: {}", data.claims.sub);
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor rust:1.99.1-trixie, y la ejecución falla si la salida no coincide.

Comando
cargo run --quiet --release
La salida debe coincidir (expresión regular)
sub: user-42

Bibliotecas