Skip to content

Tier 2 cross-listener dedup: BuildDenyEvent, inboundSessionID, rekey method #413

Description

@huang195

Background

Followup from PR #412 (proxy listener parity). After that PR landed:

  • Snapshot/derive helpers (Snapshot{A2A,MCP,Inference,Invocations,Plugins,Identity}, DeriveError, DurationSince) are shared in authlib/pipeline/snapshot.go.
  • extractBearer shared in authlib/auth/, writeRejection shared in authlib/listener/httpx/.

Remaining duplication across listeners that we deferred:

Tier 2 dedup candidates

recordInboundReject / recordOutboundReject

Live in three places with ~90% identical bodies:

  • authlib/listener/extproc/server.go (extproc takes a direction string param + builds Envoy ImmediateResponse alongside the SessionEvent)
  • authlib/listener/forwardproxy/server.go
  • authlib/listener/reverseproxy/server.go

Each builds a SessionEvent{Phase: SessionDenied, Invocations, Identity, Plugins, StatusCode, Error, Host, …}. Total duplicated logic ~60 LOC.

Proposed shape:

// pipeline/deny_event.go
func BuildDenyEvent(pctx *Context, action Action, dir Direction, phase SessionPhase) SessionEvent {
    // ... shared construction ...
}

Listeners then become:

s.Sessions.Append(sid, pipeline.BuildDenyEvent(pctx, action, pipeline.Inbound, pipeline.SessionDenied))

inboundSessionID(pctx)

extproc has it as a helper; reverseproxy inlines a slightly-different version (the proxy version falls back to ActiveSession() before defaulting; extproc goes straight to default). Pick one canonical version and put it in authlib/session or authlib/pipeline so the bucketing rule is consistent.

rekeyInboundSession ↔ inline rekey in reverseproxy

PR #412 mirrored the rekey logic in reverseproxy. Could become (*session.Store).RekeyOnA2AContextID(pctx) so it's a single method instead of conditional inline.

Why a separate PR

The shared homes for these helpers (pipeline.BuildDenyEvent, session.Store.RekeyOnA2AContextID, etc.) need a bit of design care:

  • BuildDenyEvent's signature must accommodate extproc's "also-build-the-Envoy-ImmediateResponse" coupling.
  • RekeyOnA2AContextID reaches into pctx.Extensions.A2A from a session-package method, which crosses an abstraction layer that's worth a deliberate review.

These are non-trivial enough that bundling them with PR #412 (which had a clear focus on event-field parity) would have muddied the diff.

Acceptance criteria

  • BuildDenyEvent (or equivalent shared constructor) introduced in authlib/pipeline/
  • All three listeners' recordInboundReject / recordOutboundReject reduced to a single s.Sessions.Append(sid, pipeline.BuildDenyEvent(...)) call
  • inboundSessionID consolidated into one canonical version with an explicit fallback policy documented
  • Rekey logic is one method call from any listener
  • Net diff: ~60 LOC removed, behavior unchanged, all existing tests pass

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions