AuthInterceptor class

Authenticates every run by composing an interceptor instead of configuring the agent (FR-B2). Takes an async header-builder — a closure that can mint a Bearer token, read a keychain, or refresh on demand — resolves it per run (or per retry attempt, see below), and threads the resolved headers onto the outgoing POST.

HttpAgent(
  url: endpoint,
  interceptors: [
    AuthInterceptor(headers: () async => {'Authorization': 'Bearer $token'}),
  ],
);

The header-injection seam. An Interceptor can only transform RunAgentInput → Stream<AgUiEvent>; it has no direct path to the HTTP request — the POST headers are built deep inside HttpAgent's transport. The carrier from interceptor to transport is therefore the RunAgentInput itself: the resolved headers ride on input.forwardedProps under the koel-reserved transportHeadersKey. HttpAgent's transport extracts that key, strips it from the body before encoding (so the token never reaches the wire), and merges the headers into the request with the protocol headers (Content-Type/Accept) winning. Stacked AuthInterceptors compose — the inner one's reserved map is read and merged, later keys winning.

Once per run, or once per retry attempt — by ordering, not config. The surface is exactly one named param; "per-run vs per-attempt" is the composition knob, not a flag. Plain interceptors: [AuthInterceptor(...)] resolves once per run. With HttpAgent(retry: ...), the auto-built RetryInterceptor sits outermost and re-proceeds through this interceptor on every reconnect, so the callback re-fires per attempt — the token-refresh hook.

A throwing callback becomes a terminal RunErrorEvent. Any throw from the callback is wrapped as a typed BusinessError(code: KoelErrorCode.businessAuth, cause: <original>) and surfaced through the stream; the InterceptorChain classifier passes the already-typed error through unchanged to a single terminal RunErrorEvent. The wrapper message is fixed and secret-free — no token is ever echoed into an error (architecture §5). When resolution fails, no transport connection opens (the failure precedes chain.proceed).

Open for subclassing. class … implements Interceptor (deliberately not final): Epic-5's AgnoAuthInterceptor extends AuthInterceptor.

Constructors

AuthInterceptor({required Future<Map<String, String>> headers()})
Creates an interceptor that resolves headers per run/attempt and threads the result onto the outgoing request. headers is invoked each time the chain reaches this interceptor; its returned map is merged into the request headers under transportHeadersKey (protocol headers always win). A throw from headers surfaces as RunErrorEvent(BusinessError(businessAuth)).

Properties

hashCode int
The hash code for this object.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

intercept(InterceptorChain chain, RunAgentInput input) Stream<AgUiEvent>
Wraps the execution of input, delegating to chain to reach the next interceptor (or the terminal agent). Returns the — possibly transformed — event stream the run produces.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited

Constants

transportHeadersKey → const String
The koel-namespaced forwardedProps key under which resolved auth headers ride from this interceptor to HttpAgent's transport.