refactor(local-identity): post-Stage4 cleanups and micro-fixes

- audit: chmod only on file creation, not every append (TOCTOU fix)
- jwt_utils: add extract_unverified_payload() helper
- cli: use extract_unverified_payload + JWTError instead of inline decode
- keys: extract _public_key_bytes() helper, import _b64url from jwt_utils
- security: FileNotFoundError try/except instead of path.exists() (TOCTOU fix)
- serve: cache JWK response at server init instead of per-request recompute

138 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 08:25:21 +01:00
parent 3890dca25d
commit 52d44daec2
6 changed files with 37 additions and 20 deletions

View File

@@ -114,3 +114,17 @@ def verify_token(token: str, public_key: RSAPublicKey) -> dict:
raise JWTError("token has expired")
return payload
def extract_unverified_payload(token: str) -> dict:
"""
Decode the payload of a JWT without verifying the signature.
Raises JWTError if the token is malformed.
"""
parts = token.split(".")
if len(parts) != 3:
raise JWTError("malformed token: expected 3 parts")
try:
return json.loads(_b64url_decode(parts[1]))
except Exception as exc:
raise JWTError(f"cannot decode payload: {exc}") from exc