Skip to content

@btravstack/http-server


@btravstack/http-server / session

session ​

Classes ​

CookieSchemes ​

Defined in: packages/http-server/src/cookie.ts:51

One member per composed scheme, true when that scheme reads a cookie — the graph fact csrf's default is computed from. httpServer contributes the false member that keeps the set from being the empty dependency di refuses, so a graph composing no scheme at all still starts.

A set port rather than a marker HttpModule folds: the starter never sees an application's authenticators — the module sugar provides them — so a signal read off the options record would leave a scheme it did not see unprotected. A ctx.get at start could not answer it either: di's Context has no has.

A member is owed by anything that READS OR WRITES a cookie, which is wider than "an authenticator": oidc() is not a scheme and contributes one.

Extends ​

  • PortInstance<"HttpCookieSchemes", readonly boolean[]> & object

Constructors ​

Constructor ​
ts
new CookieSchemes(): CookieSchemes;

Defined in: packages/di/dist/index.d.mts:29

Returns ​

CookieSchemes

Inherited from ​

Port.many("HttpCookieSchemes")<boolean>.constructor

Properties ​

PropertyModifierTypeInherited fromDefined in
[ID]readonly"HttpCookieSchemes"Port.many("HttpCookieSchemes").[ID]packages/di/dist/index.d.mts:11
[MANY]readonlytruePort.many("HttpCookieSchemes").[MANY]packages/di/dist/index.d.mts:30
[SERVICE]readonlyreadonly boolean[]Port.many("HttpCookieSchemes").[SERVICE]packages/di/dist/index.d.mts:12
manystatic readonlytruePort.many("HttpCookieSchemes").manypackages/di/dist/index.d.mts:33
portIdstatic readonly"HttpCookieSchemes"Port.many("HttpCookieSchemes").portIdpackages/di/dist/index.d.mts:32

SessionCodec ​

Defined in: packages/http-server/src/session.ts:99

Extends ​

Constructors ​

Constructor ​
ts
new SessionCodec(): SessionCodec;

Defined in: packages/di/dist/index.d.mts:15

Returns ​

SessionCodec

Inherited from ​

Port("HttpSessionCodec")<SessionCodecService>.constructor

Properties ​

PropertyModifierTypeInherited fromDefined in
[ID]readonly"HttpSessionCodec"Port("HttpSessionCodec").[ID]packages/di/dist/index.d.mts:11
[SERVICE]readonlySessionCodecServicePort("HttpSessionCodec").[SERVICE]packages/di/dist/index.d.mts:12
portIdstatic readonly"HttpSessionCodec"Port("HttpSessionCodec").portIdpackages/di/dist/index.d.mts:16

Type Aliases ​

Session ​

ts
type Session<P> = object;

Defined in: packages/http-server/src/session.ts:30

What the cookie carries: the application's own principal, and when the session ends.

The sealed payload also holds a typ marker this type does not declare — seal writes it and unseal requires it, so nothing outside this module sets or reads one. A session put back on the wire whole would carry it; send the principal, not the session.

Type Parameters ​

Type Parameter
P

Properties ​

PropertyModifierTypeDescriptionDefined in
clientId?readonlystringThe client id of the login that minted the session — two logins may share an issuer.packages/http-server/src/session.ts:53
expreadonlynumber-packages/http-server/src/session.ts:55
iatreadonlynumber-packages/http-server/src/session.ts:54
iss?readonlystringThe issuer of the login that minted the session, as that login is configured. With Session.clientId it names the login: sessionAuthenticator refuses a session from any other, so a second login's session is never read as the first's.packages/http-server/src/session.ts:51
principalreadonlyP-packages/http-server/src/session.ts:31
scopes?readonlyreadonly string[]What the login recorded the session as holding. A scheme grants the INTERSECTION of its own vocabulary with this, so a session naming a scope the scheme does not know grants nothing extra.packages/http-server/src/session.ts:44
sid?readonlystringThe provider's session id, kept for logout; absent when the session was not minted by an OIDC login.packages/http-server/src/session.ts:38
tokenRef?readonlystringOpaque server-side credential reference, never an access or refresh token.packages/http-server/src/session.ts:33

SessionCodecPins ​

ts
type SessionCodecPins = object;

Defined in: packages/http-server/src/session.ts:272

What sessionCodec is handed.

Properties ​

PropertyModifierTypeDefined in
keys?readonlyreadonly string[]packages/http-server/src/session.ts:273
ttlSec?readonlynumberpackages/http-server/src/session.ts:274

SessionCodecService ​

ts
type SessionCodecService = object;

Defined in: packages/http-server/src/session.ts:58

Properties ​

PropertyModifierTypeDescriptionDefined in
sealreadonly(session) => AsyncResult<string, never>Seals a session into a JWE. iat and exp are stamped here rather than accepted, so a caller cannot mint a session that outlives the policy.packages/http-server/src/session.ts:72
transientreadonlyobjectThe login flow's own state — a PKCE verifier, state, nonce, where to return to — sealed with the SAME keys under its own purpose marker and a five-minute lifetime. A transient read as a session, or a session read as one, is undefined: the marker is what separates them. typ, iat and exp are the CODEC's: seal writes them over whatever the state carries and unseal strips them off again, so a caller's own value under one of those three names never survives the round trip — which is what stops state built from a request deciding what the payload is.packages/http-server/src/session.ts:91
transient.sealreadonly(state) => AsyncResult<string, never>-packages/http-server/src/session.ts:92
transient.unsealreadonly(cookie) => AsyncResult<Readonly<Record<string, string>> | undefined, never>-packages/http-server/src/session.ts:93
ttlSecreadonlynumberWhat seal stamps, in seconds — the whole session, since there is no sliding re-seal. It is published because a login has to write the same number into the cookie's Max-Age: a browser holding the cookie longer than the payload lives is a caller who looks anonymous with a cookie still attached, and one holding it for less is a session cut short by the wrapper rather than by the policy.packages/http-server/src/session.ts:67
unsealreadonly(cookie) => AsyncResult<Session<unknown> | undefined, never>The cookie's value, opened with whichever key still holds it. Every failure — no cookie, not a JWE, a key that is gone, a tampered ciphertext, a session past its exp — is the same undefined: an anonymous caller, never an error and never a hint about which of those it was.packages/http-server/src/session.ts:79

SessionOptions ​

ts
type SessionOptions<P, Scopes, Pre> = object;

Defined in: packages/http-server/src/session.ts:346

Type Parameters ​

Type ParameterDefault type
P-
Scopes extends readonly string[]-
Pre extends stringstring

Properties ​

PropertyModifierTypeDescriptionDefined in
clientId?readonlystringThat login's client id — pins <prefix>_CLIENT_ID. A session from another issuer, another client, or none is refused.packages/http-server/src/session.ts:371
issuer?readonlystringThe issuer of the login whose sessions this scheme accepts — pins <prefix>_ISSUER, the variable that login reads too.packages/http-server/src/session.ts:366
principal?readonly(session) => P | undefinedWhat the session makes the caller. Answering undefined refuses it — the hook for a session this endpoint will not take, and the default's own answer to a session sealed with no principal at all.packages/http-server/src/session.ts:361
scopes?readonlyScopesThe scopes this scheme can grant, and the only place they are written — jwtAuthenticator's rule, for jwtAuthenticator's reason. The grant is the INTERSECTION of this vocabulary with what the session carries. Omit it entirely for a scheme with no scopes.packages/http-server/src/session.ts:353
tokens?readonlyPortClassOf<string, OidcTokensService>Validate server-side session revocation with this login’s token manager.packages/http-server/src/session.ts:355
variablePrefix?readonlyPreThe prefix of both variables. Default HTTP_OIDC, oidc()'s own — a scheme pairs with its login by naming the same prefix.packages/http-server/src/session.ts:376

Variables ​

DEFAULT_TTL_SEC ​

ts
const DEFAULT_TTL_SEC: 43200 = 43_200;

Defined in: packages/http-server/src/session.ts:102

Twelve hours, fixed: there is no sliding re-seal, so this is the whole session.


ts
const SESSION_COOKIE: "__Host-session" = "__Host-session";

Defined in: packages/http-server/src/session.ts:344

The cookie the session travels on, and there is no option to rename it.

__Host- is a prefix the BROWSER enforces — Secure, Path=/, no Domain — which is the guarantee, and it is the name oidc() writes as well: the scheme that READS the cookie and the answerer that SEALS it must agree, and two options that must agree is the shape where they silently do not. One constant both name.


TRANSIENT_TTL_SEC ​

ts
const TRANSIENT_TTL_SEC: 300 = 300;

Defined in: packages/http-server/src/session.ts:105

Five minutes, and not an option: a login that takes longer is a login to start again.

Functions ​

cookieScheme() ​

ts
function cookieScheme(): AnyProvider;

Defined in: packages/http-server/src/cookie.ts:63

What every cookie surface contributes — defineHttp for a scheme whose description says it reads one, and oidc() for itself, which is not a scheme at all but reads __Host-oidc and serves a state-changing POST /logout.

"Whatever touches a cookie contributes" is the rule, not "whatever authenticates": the narrower reading left a root composing oidc() and sessionCodec() without sessionAuthenticator serving that logout with CSRF off.

Returns ​

AnyProvider


sessionAuthenticator() ​

ts
function sessionAuthenticator<P>(): <O>(options) => Authenticator<P, ScopesOf<O>[number], 
  | SessionCodec
  | EnvReading<
  | Unpinned<O, "issuer", `${PrefixOf<O, "HTTP_OIDC">}_ISSUER`>
  | Unpinned<O, "clientId", `${PrefixOf<O, "HTTP_OIDC">}_CLIENT_ID`>, 
  | MaybePinned<O, "issuer", `${PrefixOf<O, "HTTP_OIDC">}_ISSUER`>
  | MaybePinned<O, "clientId", `${PrefixOf<O, "HTTP_OIDC">}_CLIENT_ID`>>
  | O extends object ? InstanceType<Extract<T, AnyPort>> : never, ConfigInvalid>;

Defined in: packages/http-server/src/session.ts:408

The session-cookie scheme: a third one beside jwtAuthenticator and apiKeyAuthenticator, so requires: [{ session: [] }] on a fragment route and authenticated({ session: [...] }) on a procedure need nothing new.

ts
export const browserAuth = sessionAuthenticator<Identity>()({ scopes: ["orders:export"] });

The cookie is SESSION_COOKIE and cannot be renamed: oidc() seals that name, so a knob here would be one half of a pair that must agree.

It injects SessionCodec rather than holding keys of its own, so a root composing this scheme without sessionCodec() is di's own unmet need naming SessionCodec — and the codec that reads a cookie is the very one that sealed it.

It accepts only sessions its own login minted: the issuer and client id are sealed in the session and must equal this scheme's, bound like oidc()'s from HTTP_OIDC_ISSUER and HTTP_OIDC_CLIENT_ID, or under the same variablePrefix. Two logins share one cookie and one key list, so the name of a cookie could not keep them apart.

No cookie, a cookie no key opens, a session past its exp, a session from another issuer and a principal the application declined are ONE answer: Unauthenticated, carrying no reason, which is the codec's own rule one layer up.

Type Parameters ​

Type Parameter
P

Returns ​

<O>(options) => Authenticator<P, ScopesOf<O>[number], | SessionCodec | EnvReading< | Unpinned<O, "issuer", `${PrefixOf<O, "HTTP_OIDC">}_ISSUER`> | Unpinned<O, "clientId", `${PrefixOf<O, "HTTP_OIDC">}_CLIENT_ID`>, | MaybePinned<O, "issuer", `${PrefixOf<O, "HTTP_OIDC">}_ISSUER`> | MaybePinned<O, "clientId", `${PrefixOf<O, "HTTP_OIDC">}_CLIENT_ID`>> | O extends object ? InstanceType<Extract<T, AnyPort>> : never, ConfigInvalid>


sessionCodec() ​

ts
function sessionCodec<O>(pins?): Provider<SessionCodec, ConfigInvalid, EnvReading<Unpinned<O, "keys", "HTTP_SESSION_KEYS">, MaybePinned<O, "keys", "HTTP_SESSION_KEYS">>> & object;

Defined in: packages/http-server/src/session.ts:288

The cookie codec, from HTTP_SESSION_KEYS — a comma-separated list of base64url 32-byte keys. The first seals, every one unseals, so rotation is append, promote, drop — one rollout each, so a key is unsealable on every replica before any replica seals with it; a cookie sealed with a dropped key is anonymous rather than an error.

A key that is not 32 bytes is a ConfigInvalid naming the variable, at boot, rather than a failure at the first request.

Type Parameters ​

Type ParameterDefault type
O extends SessionCodecPinsRecord<never, never>

Parameters ​

ParameterType
pins?O & OptionsAs<O, SessionCodecPins>

Returns ​

Provider<SessionCodec, ConfigInvalid, EnvReading<Unpinned<O, "keys", "HTTP_SESSION_KEYS">, MaybePinned<O, "keys", "HTTP_SESSION_KEYS">>> & object

Released under the MIT License.