Transports
WIPHow sessions ride on the request - Cookie, Bearer, JWT, Composite, and DPoP binding.
What is a transport?
A transport is the bridge between the in-memory Session and the wire.
It owns three operations:
interface AuthTransport.ITransport {
extract(req: { headers: Headers }): Promise<{ kind: 'session-id' | 'jwt'; value: string } | null>
issue(session: Session, intents: Intent[]): Intent[]
revoke(): Intent[]
}
You wire exactly one transport into AuthEngine.config.transport - or a
CompositeTransport that tries several in order.
The four shipped transports
| Transport | What it does | When to pick it |
|---|---|---|
CookieTransport | Session id in an HttpOnly cookie. | First-party web apps. Always the safest first choice. |
BearerTransport | Opaque token in Authorization: Bearer. | Mobile / native / server-to-server. |
JwtTransport | Stateless JWT + opaque refresh cookie. | High-throughput APIs, microservices, OIDC issuer. |
CompositeTransport | Try several transports in order. | Mixed web + API workloads on one origin. |
Optionally bind any transport to a client public key with the DPoP verifier (RFC 9449) for sender-constrained tokens.
Quick comparison
| Cookie | Bearer | JWT | |
|---|---|---|---|
| Stateful (requires session store lookup)? | yes yes | yes yes | no no |
| Survives XSS? | yes HttpOnly | (warning) depends on client storage | (warning) depends on client storage |
| Survives CSRF? | yes with __Host- + double-submit | yes no cookies, no CSRF | yes no cookies |
| Browser refresh keeps you signed in? | yes yes | yes if client persists | yes if refresh cookie set |
| Token revocation? | instant (delete row) | instant (delete row) | only at refresh (until JWT exp) |
| Issuing cost | one row write | one row write | two row writes + JWT sign |
| Per-request cost | one row lookup | one row lookup | signature verify only |
| Use case | first-party web | mobile / SPA cross-origin | microservices |
Picking a transport
Decision tree:
Is this a first-party web app on a single origin?
-> Cookie
Is this a mobile or native client?
-> Bearer (store the token in the OS keychain)
Is this a microservice fleet that needs to verify tokens locally without a session store hit?
-> JWT (add DPoP for sender-constrained binding)
Is this an API that also serves a first-party web app on the same origin?
-> Composite { primary: Cookie, fallback: [Bearer] }
Anatomy of an Intent
When a provider sign-in succeeds, the runtime emits Intent[]. The
transport translates intents into wire commands:
type Intent =
| { kind: 'startSession'; session: Session } // mint a session, return the cookie / token
| { kind: 'rotateSession'; session: Session } // change the carrier - fixation defence
| { kind: 'revokeSession'; sessionId: string }
| { kind: 'redirect'; url: string; status?: 302 | 303 } // OAuth provider hop
| { kind: 'json'; status: number; body: unknown }
| { kind: 'setCookie'; cookie: SetCookieDirective }
| { kind: 'header'; name: string; value: string }
The framework adapter (server/express, server/hono, ...) walks the
intents in order and converts them into native Response calls.
You almost never need to construct intents by hand - providers,
flows, and the sessions facet do it for you.