跳到主要内容
ecdsa.com
Python

Authlib (authlib.jose)

Authlib 的 JOSE 模块,是 Authlib 的 OAuth 和 OpenID Connect 客户端与服务器底层的令牌层:自 1.9 起已弃用,推荐改用 joserfc;它解码时不校验声明,而且一旦把 alg: none 列入允许的算法,就会接受它。
许可证
BSD-3-Clause
检查的版本
1.9.1 ·
源代码
github.com/authlib/authlib
文档
docs.authlib.org/en/latest/

兼容性概览

JOSE 与 JWT

JWS 算法
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

Authlib 是面向 Flask、Django、Starlette 和 httpx 的完整 OAuth 2.0 与 OpenID Connect 工具包,authlib.jose 则是它赖以构建的 JWS、JWE、JWK 和 JWT 层。在 1.9.1 中导入它会输出 authlib.jose module is deprecated, please use joserfc instead.;维护者已把 JOSE 代码移到独立的 joserfc 包中,旧模块为保持兼容会保留到 2.0。现有代码仍能运行,但新代码应该基于 joserfc 编写。

最容易让人出错的细节是:decode() 只检查签名。一个一小时前就已过期的令牌,decode() 照样返回,没有任何报错;只有调用 claims.validate() 时才会检查过期时间、颁发者和受众——而且要在 claims_options 中把 exp 和 iss 标记为必需(essential),否则缺少它们的令牌也会通过。除非向 validate() 传入宽限时间(leeway),否则不留任何宽限。

算法的处理方式取决于所用的对象。模块级的 jwt 接受它所知道的所有算法,并按密钥类型选择算法,因此用 EC 密钥验证 RS256 令牌时,在导入密钥这一步就失败了;而 JsonWebToken(["ES256"]) 实例只允许你列出的算法。只要列出 none,未签名的令牌就能验证通过,因此切勿让配置中的值最终进入这个列表。看起来像 PEM 公钥的 HMAC 密钥会被拒绝,并报错 This key may not be safe to import;ES256 也会拒绝 P-384 密钥。

1.9.1 支持的算法涵盖 HS、RS、PS 和 ES 系列,以及 ES256K 和 EdDSA,但不支持 Ed25519 或 Ed448 这两个名称。ES256 签名是 64 字节的原始 r‖s;DER 签名会报错 bad_signature,可以借助 JWT 调试器和 DER ⇄ 原始格式转换器诊断。要查看某个身份提供商的令牌和密钥是什么样子,可以使用 OIDC 令牌校验器和 JWKS 检测。

示例使用受限的 JsonWebToken 实例,并显式校验声明——这正是模块级 API 省略掉的两个步骤。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/authlib/run.sh 于2026年10月20日在容器镜像 python:3.14.8-slim-trixie 中运行了 Authlib (authlib.jose) 1.9.1(运行时为 cryptography 50.0.2);本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • JOSE 模块的状态

    deprecated

    在 1.9.1 中导入 authlib.jose 会输出 authlib.jose module is deprecated, please use joserfc instead.

    检查记录
    fact jose-module-status = deprecated
    evidence jose-module-status: authlib.jose module is deprecated, please use joserfc instead.
  • JWS 算法

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    256/384/512 位的 HS、RS、PS 和 ES,以及 ES256K 和 EdDSA,签名与验证均成功;Ed25519 和 Ed448 抛出 UnsupportedAlgorithmError。

    检查记录
    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
  • ES256 签名编码

    raw

    ES256 签名是 64 字节的原始 r‖s。

    检查记录
    fact jws-ecdsa-encoding = raw
  • JWS 中的 DER 签名

    rejected

    带 DER 编码 ES256 签名的令牌会报错 BadSignatureError: bad_signature。

    检查记录
    fact jws-der-signature = rejected
    evidence jws-der-signature: BadSignatureError: bad_signature: 
  • 算法允许列表

    from-key

    模块级的 jwt 接受它支持的、适用于该密钥类型的任何算法:ES256 令牌用 EC 密钥验证通过,而用同一个 EC 密钥验证 RS256 令牌时报错 Invalid data for importing key。

    检查记录
    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
  • 未签名的令牌(alg none)

    opt-in

    默认的 jwt 对 alg: none 抛出 UnsupportedAlgorithmError,但 JsonWebToken(["none"]) 会让未签名的令牌验证通过。

    检查记录
    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
  • 把公钥用作 HMAC 密钥

    rejected

    把 PEM 公钥用作 HS256 密钥时,无论使用默认对象还是受限实例,都会抛出 ValueError: This key may not be safe to import。

    检查记录
    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
  • 按 alg 检查曲线

    enforced

    用 P-384 密钥签名 ES256 时抛出 Key for "ES256" not supported, only "P-256" allowed。

    检查记录
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ValueError: Key for "ES256" not supported, only "P-256" allowed
  • 解码时校验声明

    no

    对于一小时前就已过期的令牌,decode() 照样返回了它的声明;只有 claims.validate() 才会检查 exp、nbf、iss 和 aud。

    检查记录
    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
  • 默认时钟偏差

    0s

    validate() 默认不留宽限时间:三秒前过期的令牌被拒绝了。

    检查记录
    fact jwt-clock-skew = 0s
  • 是否要求过期时间声明

    no

    不含 exp 的令牌通过了 validate();要强制要求它,需在 claims_options 中把它标记为必需。

    检查记录
    fact jwt-exp-required = no

用法

用只允许 ES256 的实例签发并验证 ES256 令牌,然后显式校验声明。
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"])

代码片段测试工具会在容器镜像 python:3.14.8-slim-trixie 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
pip install --quiet --root-user-action=ignore Authlib==1.9.1 cryptography==50.0.2
命令
python usage.py
输出必须匹配(正则表达式)
sub: user-42

库