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

@@ -78,6 +78,7 @@ def make_handler(
so multiple test servers can run concurrently without sharing state.
"""
codes: dict = {}
jwks_response = {"keys": [jwk_public(private_key)]}
class _Handler(OIDCHandler):
_private_key = private_key
@@ -85,6 +86,7 @@ def make_handler(
_token_ttl = token_ttl
_codes = codes
_scheme = scheme
_jwks = jwks_response
return _Handler
@@ -100,6 +102,7 @@ class OIDCHandler(http.server.BaseHTTPRequestHandler):
_token_ttl: int = 3600
_codes: dict = {}
_scheme: str = "https"
_jwks: dict = {}
def log_message(self, fmt: str, *args) -> None:
pass # silence default Apache-style logging
@@ -161,7 +164,7 @@ class OIDCHandler(http.server.BaseHTTPRequestHandler):
# ---------------------------------------------------------------- #
def _handle_jwks(self) -> None:
self._send_json({"keys": [jwk_public(self._private_key)]})
self._send_json(self._jwks)
# ---------------------------------------------------------------- #
# Endpoint: GET /auth — display login form #