Connect P04 audited recovery to fresh-MFA platform browser flow
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 54s
Account journey acceptance / journeys (push) Successful in 5s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
tegwick 2026-09-13 21:05:33 +02:00
parent b2cce8dd7c
commit 58e07dd4df
7 changed files with 201 additions and 7 deletions

View file

@ -17,6 +17,7 @@ from urllib.request import Request, urlopen
class PendingLogin:
verifier: str
created_at: float
recovery: bool = False
@dataclass
@ -24,6 +25,8 @@ class BrowserSession:
claims: Mapping[str, Any]
expires_at: float
csrf_token: str = ""
id_token: str = ""
recovery: bool = False
class OIDCClient:
@ -48,11 +51,11 @@ class OIDCClient:
self.pending: dict[str, PendingLogin] = {}
self.sessions: dict[str, BrowserSession] = {}
def begin(self, *, tenant_hint: str | None = None) -> str:
def begin(self, *, tenant_hint: str | None = None, recovery: bool = False) -> str:
state = secrets.token_urlsafe(32)
verifier = secrets.token_urlsafe(64)
challenge = _b64(hashlib.sha256(verifier.encode("ascii")).digest())
self.pending[state] = PendingLogin(verifier=verifier, created_at=time.time())
self.pending[state] = PendingLogin(verifier=verifier, created_at=time.time(), recovery=recovery)
self._prune()
parameters = {
'response_type': 'code',
@ -63,6 +66,8 @@ class OIDCClient:
'code_challenge': challenge,
'code_challenge_method': 'S256',
}
if recovery:
parameters.update(prompt="login", max_age="0", acr_values="aal2")
if tenant_hint:
parameters["tenant_hint"] = tenant_hint
return f"{self.issuer}/authorize?{urlencode(parameters)}"
@ -100,6 +105,8 @@ class OIDCClient:
claims=claims,
expires_at=expiry,
csrf_token=secrets.token_urlsafe(32),
id_token=token,
recovery=pending.recovery,
)
self._prune()
return session_id