Authlib — полноценный набор инструментов OAuth 2.0 и OpenID Connect для Flask, Django, Starlette и httpx, а 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), иначе токен без них тоже пройдёт проверку. Допустимое расхождение часов (leeway) равно нулю, если не передать его в validate().
Обработка алгоритмов зависит от того, какой объект вы используете. Объект jwt уровня модуля принимает любой известный ему алгоритм и выбирает его по типу ключа, поэтому проверка токена RS256 с ключом EC завершилась ошибкой уже на импорте ключа; экземпляр JsonWebToken(["ES256"]) допускает только перечисленные алгоритмы. Достаточно внести в этот список none, чтобы неподписанный токен прошёл проверку, поэтому никогда не допускайте, чтобы в список попало значение из конфигурации. Ключи HMAC, похожие на открытые ключи PEM, отклоняются с ошибкой This key may not be safe to import, а ключ P-384 для ES256 не принимается.
В версии 1.9.1 поддерживаются алгоритмы семейств HS, RS, PS и ES, а также ES256K и EdDSA, но не имена Ed25519 и Ed448. Подпись ES256 — это 64 байта в формате raw r‖s, а проверка подписи в DER завершается ошибкой bad_signature; найти причину помогут отладчик JWT и конвертер DER ⇄ raw. Чтобы посмотреть, как выглядят токены и ключи поставщика удостоверений, воспользуйтесь валидатором токенов OIDC и проверкой JWKS.
Пример использует экземпляр JsonWebToken с ограниченным списком алгоритмов и явно проверяет утверждения — это те два шага, которые пропускает API уровня модуля.