Saltar al contenido
ecdsa.com
Python

Authlib (authlib.jose)

El módulo JOSE de Authlib, la capa de tokens que hay bajo los clientes y servidores OAuth y OpenID Connect de Authlib: obsoleto desde la 1.9 en favor de joserfc, decodifica sin validar los claims y admite alg: none en cuanto lo añades a la lista.
Licencia
BSD-3-Clause
Versión comprobada
1.9.1 ·
Código fuente
github.com/authlib/authlib
Documentación
docs.authlib.org/en/latest/

Compatibilidad de un vistazo

JOSE y JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

Authlib es un kit completo de OAuth 2.0 y OpenID Connect para Flask, Django, Starlette y httpx, y authlib.jose es la capa de JWS, JWE, JWK y JWT sobre la que se construyó. Importarlo en la 1.9.1 emite authlib.jose module is deprecated, please use joserfc instead.; los mantenedores trasladaron el código JOSE al paquete independiente joserfc, y el módulo antiguo se conserva por compatibilidad hasta la 2.0. El código existente sigue funcionando, pero el código nuevo debería usar joserfc.

El detalle que más problemas causa es que decode() solo comprueba la firma. Un token que había caducado una hora antes salió de decode() sin ninguna queja; la caducidad, el emisor y la audiencia solo se comprueban cuando llamas a claims.validate(), con un claims_options que marque exp e iss como esenciales, porque, si no, un token sin ellos también pasa. La tolerancia de reloj es cero salvo que pases una a validate().

El tratamiento de los algoritmos depende del objeto que uses. El jwt de nivel de módulo acepta todos los algoritmos que conoce y elige según el tipo de clave, así que un token RS256 presentado con una clave EC falló al importar la clave; una instancia JsonWebToken(["ES256"]) solo admite los que incluyas en la lista. Basta con incluir none en la lista para que un token sin firmar se verifique, así que nunca dejes que un valor de configuración acabe en esa lista. Las claves HMAC que parecen claves públicas PEM se rechazan con This key may not be safe to import, y ES256 rechaza una clave P-384.

Los algoritmos admitidos en la 1.9.1 abarcan las familias HS, RS, PS y ES, ES256K y EdDSA, pero no los nombres Ed25519 ni Ed448. Las firmas ES256 son 64 bytes de r‖s en bruto; una firma DER falla con bad_signature, algo que ayudan a diagnosticar el decodificador de JWT y el conversor DER ⇄ en bruto. Para comprobar cómo son los tokens y las claves de un proveedor de identidad, usa el validador de tokens OIDC y el comprobador de JWKS.

El ejemplo usa una instancia restringida de JsonWebToken y valida los claims de forma explícita: los dos pasos que la API de nivel de módulo omite.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/authlib/run.sh ejecutó Authlib (authlib.jose) 1.9.1 en cryptography 50.0.2 en la imagen de contenedor python:3.14.8-slim-trixie el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Estado del módulo JOSE

    deprecated

    Importar authlib.jose en la 1.9.1 emite authlib.jose module is deprecated, please use joserfc instead.

    Registrado por la comprobación
    fact jose-module-status = deprecated
    evidence jose-module-status: authlib.jose module is deprecated, please use joserfc instead.
  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    Con HS, RS, PS y ES de 256/384/512 bits, ES256K y EdDSA se firmó y se verificó; Ed25519 y Ed448 lanzaron UnsupportedAlgorithmError.

    Registrado por la comprobación
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA
    evidence jws-algorithms: Ed25519: UnsupportedAlgorithmError: unsupported_algorithm: 
    evidence jws-algorithms: Ed448: UnsupportedAlgorithmError: unsupported_algorithm: 
    evidence jws-algorithms: algorithms the module-level authlib.jose.jwt accepts: ES256, ES256K, ES384, ES512, EdDSA, HS256, HS384, HS512, PS256, PS384, PS512, RS256, RS384, RS512
  • 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

    Un token con una firma ES256 codificada en DER falla con BadSignatureError: bad_signature.

    Registrado por la comprobación
    fact jws-der-signature = rejected
    evidence jws-der-signature: BadSignatureError: bad_signature: 
  • Lista de algoritmos permitidos

    from-key

    El jwt de nivel de módulo acepta cualquier algoritmo que admita para el tipo de clave: un token ES256 se verificó con una clave EC, mientras que un token RS256 con esa misma clave EC falló con Invalid data for importing key.

    Registrado por la comprobación
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: authlib.jose.jwt.decode on ES256: verified; RS256 token with an EC key: ValueError: Invalid data for importing key
  • Tokens sin firma (alg none)

    opt-in

    El jwt predeterminado lanza UnsupportedAlgorithmError con alg: none, pero JsonWebToken(["none"]) verifica un token sin firmar.

    Registrado por la comprobación
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: default jwt with an EC key: UnsupportedAlgorithmError: unsupported_algorithm: 
    evidence jwt-alg-none: JsonWebToken(['none']): verified
  • Clave pública usada como secreto HMAC

    rejected

    Una clave pública PEM usada como clave HS256 lanza ValueError: This key may not be safe to import, tanto con el objeto predeterminado como con una instancia restringida.

    Registrado por la comprobación
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: JsonWebToken(['HS256']) with PEM text as key: ValueError: This key may not be safe to import
    evidence jwt-hmac-public-key: default jwt with PEM text as key: ValueError: This key may not be safe to import
  • Curva cotejada con alg

    enforced

    Firmar ES256 con una clave P-384 lanza Key for "ES256" not supported, only "P-256" allowed.

    Registrado por la comprobación
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ValueError: Key for "ES256" not supported, only "P-256" allowed
  • Claims validados al decodificar

    no

    decode() devolvió los claims de un token que había caducado una hora antes; solo claims.validate() comprueba exp, nbf, iss y aud.

    Registrado por la comprobación
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: decode() of a token expired an hour ago without validate(): returned claims
  • Desfase de reloj predeterminado

    0s

    validate() no aplica ninguna tolerancia de reloj por defecto: se rechazó un token que había caducado 3 segundos antes.

    Registrado por la comprobación
    fact jwt-clock-skew = 0s
  • Claim de caducidad obligatorio

    no

    Un token sin exp superó validate(); márcalo como esencial en claims_options para exigirlo.

    Registrado por la comprobación
    fact jwt-exp-required = no

Uso

Emite y verifica un token ES256 con una instancia restringida a ES256 y después valida los claims de forma explícita.
import time

from authlib.jose import JsonWebKey, JsonWebToken

key = JsonWebKey.generate_key("EC", "P-256", is_private=True)
public_jwk = key.as_dict(is_private=False)

# A JsonWebToken instance restricted to ES256 instead of the permissive module-level jwt.
es256 = JsonWebToken(["ES256"])
token = es256.encode({"alg": "ES256"}, {"sub": "user-42", "iss": "https://auth.example.com", "exp": int(time.time()) + 600}, key)

claims = es256.decode(
    token,
    public_jwk,
    claims_options={"iss": {"essential": True, "value": "https://auth.example.com"}, "exp": {"essential": True}},
)
claims.validate(leeway=30)  # decode() checks only the signature; validate() checks the claims
print("sub:", claims["sub"])

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

Configuración
pip install --quiet --root-user-action=ignore Authlib==1.9.1 cryptography==50.0.2
Comando
python usage.py
La salida debe coincidir (expresión regular)
sub: user-42

Bibliotecas