Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Sequence
from .auth_jwt_decode_jwt import check_jwt_secret, split_jwt ← decodeJwt, another function of this group · built into the same file, even by a slim install
from .auth_jwt_types import JwtVerification
from .crypto_constant_time_equal import constant_time_equal ← from crypto.constant-time-equal ^1.0.0 · built alongside by fune
from .crypto_hmac_sha256 import hmac_sha256 ← from crypto.hmac-sha256 ^1.0.0 · built alongside by fune
from .encoding_utf8_utf8_encode import utf8_encode
def _fail(error: str, message: str) -> JwtVerification:
return JwtVerification(valid=False, claims=None, error=error, message=message)
def verify_jwt(token: str, secret: Sequence[int], now: int, leeway_seconds: int) -> JwtVerification:
"""Check an HS256 token: shape, algorithm (so "none" and algorithm-confusion
tokens are refused before any key is used), signature in constant time,
then exp, nbf and iat against now with leeway. A bad token is an answer,
never an exception; only a bad secret, now or leeway raises."""
check_jwt_secret(secret)
if isinstance(now, bool) or not isinstance(now, int):
raise TypeError("now must be a whole number of Unix seconds")
if isinstance(leeway_seconds, bool) or not isinstance(leeway_seconds, int) or leeway_seconds < 0:
raise ValueError("leewaySeconds must be a whole number, 0 or more")
parts = split_jwt(token)
if parts is None:
return _fail("malformed_token", "the token is not a well-formed JWT")
header, claims, signing_input, signature = parts
if header.get("alg") != "HS256":
return _fail("unsupported_algorithm", "only HS256 tokens are accepted")
if "crit" in header:
return _fail("unsupported_algorithm", "the token requires header extensions (crit) this verifier does not support")
expected = hmac_sha256(secret, utf8_encode(signing_input))
if not constant_time_equal(expected, signature):
return _fail("invalid_signature", "the token's signature does not match")
for name in ("exp", "nbf", "iat"):
if name in claims and (isinstance(claims[name], bool) or not isinstance(claims[name], (int, float))):
return _fail("invalid_claims", "the token's %s claim is not a number" % name)
# RFC 7519 4.1.4: the current time MUST be before exp.
if "exp" in claims and now >= claims["exp"] + leeway_seconds:
return _fail("token_expired", "the token has expired")
# RFC 7519 4.1.5: the current time MUST be at or after nbf.
if "nbf" in claims and now + leeway_seconds < claims["nbf"]:
return _fail("token_not_yet_valid", "the token is not valid yet")
if "iat" in claims and claims["iat"] > now + leeway_seconds:
return _fail("token_issued_in_future", "the token was issued in the future")
return JwtVerification(valid=True, claims=claims, error=None, message=None)