70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
"""
|
|
Shared test helpers for building JWTs.
|
|
|
|
Tokens are built with the project's own hmac_sign so the tests exercise the
|
|
real signing path and stay dependency free. A fixed clock is exposed so the
|
|
time based checks are deterministic.
|
|
"""
|
|
|
|
import base64
|
|
import json
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from jwt_auditor.signatures import hmac_sign
|
|
|
|
# A fixed "now" used across time based tests: 2026-01-01 00:00:00 UTC.
|
|
FIXED_NOW = 1767225600.0
|
|
ONE_HOUR = 3600.0
|
|
ONE_DAY = 86400.0
|
|
|
|
|
|
def b64url(data: bytes) -> str:
|
|
"""Encode bytes as base64url without padding, the way JWT does."""
|
|
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
|
|
|
|
|
def _segment(obj: dict[str, Any]) -> str:
|
|
"""Encode a JSON object as a base64url JWT segment."""
|
|
return b64url(json.dumps(obj, separators = (",", ":")).encode("utf-8"))
|
|
|
|
|
|
def build_hs_token(
|
|
payload: dict[str,
|
|
Any],
|
|
secret: str = "secret",
|
|
alg: str = "HS256",
|
|
header: dict[str,
|
|
Any] | None = None,
|
|
) -> str:
|
|
"""Build an HMAC signed token using the given secret."""
|
|
head = header or {"alg": alg, "typ": "JWT"}
|
|
signing_input = f"{_segment(head)}.{_segment(payload)}".encode("ascii")
|
|
signature = hmac_sign(signing_input, secret.encode("utf-8"), alg)
|
|
return f"{_segment(head)}.{_segment(payload)}.{b64url(signature)}"
|
|
|
|
|
|
def build_none_token(payload: dict[str, Any]) -> str:
|
|
"""Build an unsigned alg none token ending in a trailing dot."""
|
|
head = {"alg": "none", "typ": "JWT"}
|
|
return f"{_segment(head)}.{_segment(payload)}."
|
|
|
|
|
|
def build_unsigned_token(payload: dict[str, Any], alg: str = "HS256") -> str:
|
|
"""Build a token that declares a real alg but carries no signature."""
|
|
head = {"alg": alg, "typ": "JWT"}
|
|
return f"{_segment(head)}.{_segment(payload)}."
|
|
|
|
|
|
def build_bare_alg_token(payload: dict[str, Any], alg: str) -> str:
|
|
"""Build a token with an arbitrary alg and a dummy signature segment."""
|
|
head = {"alg": alg, "typ": "JWT"}
|
|
return f"{_segment(head)}.{_segment(payload)}.{b64url(b'dummy-signature')}"
|
|
|
|
|
|
@pytest.fixture
|
|
def now() -> float:
|
|
"""The fixed clock value for deterministic time checks."""
|
|
return FIXED_NOW
|