- 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.
110 lines
3.0 KiB
Python
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"]
|