Skip to content

symfonic.capabilities.human.refusals

refusals

Reading a signer's refusal without knowing what a signer is (EMAP-6).

verify denies for several reasons and this package must not care which — except for one distinction that is not about the token at all. When the verification keyset is unavailable the signer has no choice but to deny, yet the holder's token is provably still redeemable; telling them "unauthorized" sends their retry logic in exactly the wrong direction, and T3.4.5 recorded that consequence without resolving it.

The resolution is narrow on purpose: a closed set of availability codes, read from the cause's declared code and nothing else. Matching on the cause's type would couple this capability to the switching package it deliberately only knows through a port; matching on its message would be the layer-boundary name-matching T1.2.3 rejected. Everything outside the set stays an indistinguishable denial, which is the security property.

availability_code

availability_code(exc: BaseException) -> str | None

The cause's availability code, or None for every other refusal.

Source code in src/symfonic/capabilities/human/refusals.py
def availability_code(exc: BaseException) -> str | None:
    """The cause's availability code, or ``None`` for every other refusal."""
    code = getattr(exc, "code", None)
    return code if isinstance(code, str) and code in AVAILABILITY_CODES else None