# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-silent-session-identity-drift-when-resuming-an-mcp-sse-session-across-the-streamable-http-transport-boundary-under-oauth-2-1.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-silent-session-identity-drift-when-resuming-an-mcp-sse-session-across-the-streamable-http-transport-boundary-under-oauth-2-1.warrant.json
# Markdown SHA-256:   186ac28e0e1cd03e44897f16e9dd6f61bc4fa378719b59d51d907181a83d236a
# Code AST SHA-256:   56abb9e9b4437208ccec0743fbcf886109fd8de042980c7e0e36ba2bb8a20c07
# Verified Classes:   SessionBindingDriftError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class SessionBindingDriftError(RuntimeError):
    """Raised when a resumed MCP session's recorded authorization binding
    does not match the binding of the token presented on the resume request."""
    pass


def resume_streamable_session(
    session_id: str,
    presented_token: dict,
    session_store: dict,
) -> dict:
    """Resume an SSE-originated session on the streamable-HTTP endpoint.

    presented_token is the decoded, signature-validated token claim set:
        {"aud": str, "sub": str, "scope": str, "exp": int}
    session_store maps session_id -> {"aud", "sub", "scope", "events"}.
    """
    record = session_store.get(session_id)
    if record is None:
        raise SessionBindingDriftError(
            f"unknown session {session_id!r}; refusing to resume"
        )

    recorded = (record["aud"], record["sub"], record["scope"])
    presented = (
        presented_token["aud"],
        presented_token["sub"],
        presented_token["scope"],
    )

    # Authentication succeeded upstream; this is the authorization-continuity
    # check that prevents silent drift across the transport boundary.
    if recorded != presented:
        # Fail closed: drop the buffered stream and force re-initialization.
        del session_store[session_id]
        raise SessionBindingDriftError(
            "session binding drift on resume: "
            f"recorded={recorded!r} presented={presented!r}; "
            "session invalidated, client must re-initialize"
        )

    return {"session_id": session_id, "events": record["events"]}
