Files
guide/src/guide/app/utils/jwt.py
Travis Vasceannie 3ecf5077c2 Refactor and Enhance Codebase for Improved Modularity and Type Safety
- Analyzed and documented architectural improvements based on the `repomix-output.md`, focusing on reducing code duplication and enhancing type safety.
- Introduced a new `ConditionalCompositeAction` class to support runtime conditional execution of action steps.
- Refactored existing action and browser helper methods to improve modularity and maintainability.
- Updated GraphQL client integration for better connection pooling and resource management.
- Enhanced error handling and diagnostics across various components, ensuring clearer feedback during execution.
- Removed outdated playbook actions and streamlined the action registry for better clarity and performance.
- Updated configuration files to reflect changes in browser host management and session handling.
- Added new tests to validate the refactored components and ensure robust functionality.
2025-12-07 11:40:34 +00:00

110 lines
3.0 KiB
Python

"""JWT utility functions.
Shared utilities for JWT parsing and validation without cryptographic verification.
These are used for extracting claims and basic format validation only.
"""
import base64
import json
import logging
from datetime import datetime, timezone
from typing import cast
_logger = logging.getLogger(__name__)
def is_jwt_format(value: str) -> bool:
"""Check if value looks like a JWT (three base64 segments separated by dots).
Performs basic structural validation without cryptographic verification.
Args:
value: String to check.
Returns:
True if the value appears to be a JWT, False otherwise.
"""
parts = value.split(".")
if len(parts) != 3:
return False
# Each part should be non-empty and contain valid base64-ish characters
return all(part and all(c.isalnum() or c in "-_=" for c in part) for part in parts)
def parse_jwt_expiry(token: str) -> datetime | None:
"""Parse exp claim from JWT payload.
Decodes the JWT payload (base64) without verification to extract
the expiration timestamp.
Args:
token: JWT token string.
Returns:
Expiration datetime (UTC) if found, None otherwise.
"""
try:
parts = token.split(".")
if len(parts) != 3:
return None
# Decode payload (second part)
payload_b64 = parts[1]
# Add padding if needed
padding = 4 - len(payload_b64) % 4
if padding != 4:
payload_b64 += "=" * padding
payload_bytes = base64.urlsafe_b64decode(payload_b64)
# json.loads returns Any; cast to object for type narrowing
decoded = cast(object, json.loads(payload_bytes.decode("utf-8")))
if not isinstance(decoded, dict):
return None
# Cast narrowed dict to typed dict for proper member access
payload = cast("dict[str, object]", decoded)
exp_value = payload.get("exp")
if isinstance(exp_value, int | float):
return datetime.fromtimestamp(exp_value, tz=timezone.utc)
except Exception as exc:
_logger.debug("Failed to parse JWT expiry: %s", exc)
return None
def decode_jwt_payload(token: str) -> dict[str, object] | None:
"""Decode JWT payload without verification.
Args:
token: JWT token string.
Returns:
Decoded payload as dict if successful, None otherwise.
"""
try:
parts = token.split(".")
if len(parts) != 3:
return None
payload_b64 = parts[1]
# Add padding if needed
padding = 4 - len(payload_b64) % 4
if padding != 4:
payload_b64 += "=" * padding
payload_bytes = base64.urlsafe_b64decode(payload_b64)
decoded = cast(object, json.loads(payload_bytes.decode("utf-8")))
if isinstance(decoded, dict):
return cast("dict[str, object]", decoded)
except Exception as exc:
_logger.debug("Failed to decode JWT payload: %s", exc)
return None
__all__ = ["is_jwt_format", "parse_jwt_expiry", "decode_jwt_payload"]