@zitadel/sdk-core 0.1.0-alpha.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,556 @@
1
+ import { JwtPayload } from "./jwt.mjs";
2
+ //#region src/middleware.d.ts
3
+ /**
4
+ * Headers that must never be forwarded to an upstream service.
5
+ * These are connection-level headers that are meaningful only between
6
+ * two directly connected peers and become invalid when proxied.
7
+ */
8
+ export declare const HOP_BY_HOP: ReadonlySet<string>;
9
+ /**
10
+ * SDK-internal headers that must never be forwarded to the upstream backend.
11
+ * These headers carry session data between the middleware and server components;
12
+ * forwarding them upstream would expose internal state and allow header injection.
13
+ */
14
+ export declare const INTERNAL_HEADERS: ReadonlySet<string>;
15
+ /**
16
+ * Returns `true` when `pathname` matches at least one entry in `routes`.
17
+ *
18
+ * An entry ending with `*` matches any path that starts with the prefix
19
+ * before the `*`. All other entries match exactly, ignoring a trailing slash
20
+ * on either side: `/profile` and `/profile/` name the same route, and several
21
+ * frameworks (Qwik Router by default) serve the slashed form as canonical. An
22
+ * exact compare would let `/profile/` past a `protectedRoutes: ["/profile"]`.
23
+ *
24
+ * @param pathname - The URL pathname to test.
25
+ * @param routes - The list of route patterns to match against.
26
+ * @returns `true` if at least one pattern matches.
27
+ */
28
+ export declare function matchesRoutes(pathname: string, routes: readonly string[]): boolean;
29
+ /**
30
+ * Where a signed-out request for a protected route is sent: `loginPath`, with
31
+ * the requested path and query string in `next`, so `<zitadel-login>` returns
32
+ * to the page as it was asked for, filters and tabs included. The fragment
33
+ * never reaches the server, so it cannot be carried.
34
+ *
35
+ * A `next` the request carries itself is left out, so a redirect never nests
36
+ * an earlier one: should the login page be protected by mistake, each pass of
37
+ * the redirect loop would otherwise wrap the last and grow the URL. The rest
38
+ * of the query keeps the encoding it was sent with.
39
+ *
40
+ * Whatever query `loginPath` has of its own is kept, except a `next`, which
41
+ * this replaces.
42
+ *
43
+ * @param loginPath - The login page, a path starting with a single `/`.
44
+ * @param requestUrl - The full URL of the signed-out request.
45
+ * @returns The URL to redirect to, on the request's origin.
46
+ */
47
+ export declare function loginRedirectUrl(loginPath: string, requestUrl: string | URL): URL;
48
+ /**
49
+ * Whether a proxied call may carry the confidential project secret.
50
+ *
51
+ * ADR 036: the browser flow needs the secret only for the handoff exchange
52
+ * (`POST /sessions/exchange`). Stamping it onto every proxied operation would
53
+ * turn the app's public `/__nextgen/*` boundary into an open relay carrying
54
+ * project-level authority for any anonymous caller.
55
+ *
56
+ * @param method - The proxied request's HTTP method.
57
+ * @param pathname - The path after the proxy prefix, e.g. `/sessions/exchange`.
58
+ * @returns `true` only for the handoff exchange.
59
+ */
60
+ export declare function requiresProjectSecret(method: string, pathname: string): boolean;
61
+ /**
62
+ * Filters upstream response headers for proxying: strips hop-by-hop headers
63
+ * and `set-cookie` (handled separately via `getSetCookie()`), and reduces
64
+ * `location` to a redirect that can only land on this app.
65
+ *
66
+ * `location` was stripped outright until the identity-provider callback
67
+ * needed it: the provider returns the browser to `/__nextgen/idp/{slug}/callback`,
68
+ * and the engine answers `303` back to the page the sign-in started on. That
69
+ * is a top-level navigation with no JavaScript in the loop, so a dropped
70
+ * `Location` leaves the browser on an empty page. Outbound redirects to a
71
+ * provider do not travel this way -- they arrive as `step.redirect_url` in a
72
+ * JSON body and the widget navigates -- so nothing legitimate points off-origin.
73
+ *
74
+ * @param upstream - The upstream response headers.
75
+ * @param selfUrl - The app's own request URL, which is both the origin a
76
+ * redirect must land on and the base a relative one resolves against.
77
+ * Omitting it keeps the old behaviour of dropping every redirect.
78
+ * @returns A new `Headers` object with filtered headers.
79
+ */
80
+ export declare function filterResponseHeaders(upstream: Headers, selfUrl?: string): Headers;
81
+ /** The cookie the server sets a session in and reads it from. */
82
+ export declare const SERVER_SESSION_COOKIE = "__nextgen_session";
83
+ /**
84
+ * The cookie an app keeps its project's session in: the server's name with the
85
+ * project id appended (ADR 073). A browser shares one cookie jar across every
86
+ * port of a host, so under the server's name alone the Console of a local
87
+ * server and every app on `localhost` would hold their sessions in one slot and
88
+ * replace each other's.
89
+ *
90
+ * Every byte of the id outside `[A-Za-z0-9_-]` is percent-encoded, so any
91
+ * configured id makes a valid cookie name. Ids the server mints need none.
92
+ */
93
+ export declare function sessionCookieName(projectId: string): string;
94
+ /**
95
+ * A session cookie a response sets. An empty `value` with `maxAge: 0` clears
96
+ * it.
97
+ */
98
+ export interface SessionCookie {
99
+ readonly name: string;
100
+ readonly value: string;
101
+ /** Seconds the browser keeps it, or `undefined` for the browser session. */
102
+ readonly maxAge: number | undefined;
103
+ readonly secure: boolean;
104
+ }
105
+ /**
106
+ * The `Set-Cookie` value for `cookie`, with the attributes the server gives its
107
+ * own session cookie: `Path=/`, `HttpOnly`, `SameSite=Lax`.
108
+ */
109
+ export declare function serializeSessionCookie(cookie: SessionCookie): string;
110
+ /**
111
+ * The `Cache-Control` of a response that sets a session cookie, as the server
112
+ * sends on its session answers: a shared cache must not hand the cookie, the
113
+ * session token it carries or its clearing, to the next visitor.
114
+ */
115
+ export declare const SESSION_COOKIE_CACHE_CONTROL = "private, no-store";
116
+ /** The browser's request as the app sees it, and as a proxy in front of it forwarded it. */
117
+ export interface BrowserRequest {
118
+ /** The app's own request URL. */
119
+ readonly requestUrl: string | URL;
120
+ /** The request's `X-Forwarded-Proto`, or `null`. */
121
+ readonly forwardedProto?: string | null;
122
+ /** The request's `X-Forwarded-Host`, or `null`. */
123
+ readonly forwardedHost?: string | null;
124
+ }
125
+ /**
126
+ * What a proxied request carries that decides its session cookies. The
127
+ * request URL and the forwarded headers decide whether a cookie needs `Secure`.
128
+ */
129
+ export interface ProxiedRequest extends BrowserRequest {
130
+ readonly method: string;
131
+ /** The path after the proxy prefix, e.g. `/sessions/me`. */
132
+ readonly path: string;
133
+ /** The query string, with its leading `?`, or `""`. */
134
+ readonly search: string;
135
+ /** The request's `Cookie` header, or `null`. */
136
+ readonly cookie: string | null;
137
+ }
138
+ /** The server a proxy forwards to, for the calls it makes of its own. */
139
+ export interface ProxyUpstream {
140
+ /** Base URL of the auth backend. */
141
+ readonly url: string;
142
+ /** How long to wait for an answer. */
143
+ readonly timeoutMs: number;
144
+ }
145
+ /** What a proxied answer sends the browser. */
146
+ export interface ProxiedAnswer {
147
+ readonly setCookies: string[];
148
+ readonly body: ReadableStream<Uint8Array> | string | null;
149
+ }
150
+ /** A proxied call's session cookies, on the way up and on the way back. */
151
+ export interface ProxiedSessionCookies {
152
+ /** The `Cookie` header to send upstream, or `null` to send none. */
153
+ readonly upstreamCookie: string | null;
154
+ /**
155
+ * The upstream answer's way back to the browser: the `Set-Cookie` values and
156
+ * the body to send. Send this body, never the upstream response's own, which
157
+ * this may have read.
158
+ */
159
+ toBrowser(upstream: Response): Promise<ProxiedAnswer>;
160
+ }
161
+ /**
162
+ * The session cookies of a call the app proxies to the server (ADR 073). The
163
+ * server knows one session cookie, `__nextgen_session`; the app keeps its
164
+ * project's session in {@link sessionCookieName}, so it neither replaces nor
165
+ * reads the session of another app or of the Console on the same host.
166
+ *
167
+ * - On the way up, the app's own session cookie goes under the server's name,
168
+ * and every other session cookie stays behind. Other cookies pass unchanged.
169
+ * - On the way back, a `Set-Cookie` of the server's session cookie, which sets
170
+ * or clears it, is renamed to the app's.
171
+ * - A session kept under the server's name goes up to one call only, the
172
+ * session probe `GET /sessions/me?project_id=<project>`, which the server
173
+ * refuses for another project's session. When the answer signs a user of
174
+ * this project in, the session is copied to the app's cookie; the server's
175
+ * cookie keeps it, since the Console may share the project.
176
+ * - When the app signs a user in (`POST /sessions/exchange`) or out
177
+ * (`DELETE /sessions/me`), a session still kept under the server's name is
178
+ * retired, or it would sign its user back in once the app's own cookie is
179
+ * gone: the server is asked about it in this project, and a session of this
180
+ * project is cleared (and on sign-out also ended), a dead one cleared, and
181
+ * one of another project, the Console's on `localhost`, left alone.
182
+ * - Without a project the cookies pass as they are: nothing says which session
183
+ * is the app's.
184
+ *
185
+ * @param request - The proxied request.
186
+ * @param projectId - The project this app signs its users in to.
187
+ * @param upstream - The server the proxy forwards to, which it asks about a
188
+ * session kept under the server's name.
189
+ */
190
+ export declare function proxySessionCookies(request: ProxiedRequest, projectId: string | undefined, upstream: ProxyUpstream): ProxiedSessionCookies;
191
+ /**
192
+ * The authenticated session for a signed-in user. Identity follows the
193
+ * user-ref vocabulary (ADR 058): render `display`, falling back to
194
+ * `identifier`, then `userId`.
195
+ */
196
+ export type NextgenSession = {
197
+ /** The user's unique identifier (`sub` claim). */
198
+ userId: string;
199
+ /**
200
+ * The user schema's designated identifier value (the login identifier,
201
+ * e.g. an email address or username), or `null` when unresolved. On the
202
+ * JWT path this carries the `email` claim.
203
+ */
204
+ identifier: string | null;
205
+ /**
206
+ * The schema property `identifier` came from (e.g. `"email"`), or `null`
207
+ * when unknown — JWT-claim identities carry no property attribution.
208
+ */
209
+ identifierProperty: string | null;
210
+ /** The user's display name rendering, or `null` when the schema designates none. */
211
+ display: string | null;
212
+ /** The raw verified JWT. */
213
+ token: string;
214
+ };
215
+ /** Auth state when the user is signed in. */
216
+ export type AuthState = {
217
+ isAuthenticated: true;
218
+ session: NextgenSession;
219
+ };
220
+ /** Auth state when the user is not signed in. */
221
+ export type UnauthState = {
222
+ isAuthenticated: false;
223
+ session: null;
224
+ };
225
+ /** Union of all possible auth states. */
226
+ export type AuthResult = AuthState | UnauthState;
227
+ /**
228
+ * The client-safe session exposed to app UI (headers, account menus).
229
+ * Identical to {@link NextgenSession} but omits `token` — the raw session
230
+ * token must never reach client-side JavaScript, whether through an SSR
231
+ * payload or a client-side fetch result.
232
+ */
233
+ export type ClientSession = {
234
+ /** The user's unique identifier (`sub` claim). */
235
+ userId: string;
236
+ /** The designated identifier value (login identifier), or `null`. */
237
+ identifier: string | null;
238
+ /** The schema property the identifier came from, or `null`. */
239
+ identifierProperty: string | null;
240
+ /** The display name rendering, or `null`. */
241
+ display: string | null;
242
+ };
243
+ /** Client-safe auth state when the user is signed in. */
244
+ export type ClientAuthState = {
245
+ isAuthenticated: true;
246
+ session: ClientSession;
247
+ };
248
+ /**
249
+ * Union of all possible client-safe auth states. Returned by the client
250
+ * session reads (`useAuth()` in sdk-nuxt, `getSession()` in sdk-next).
251
+ * Token is intentionally absent — use the server-side helpers when the raw
252
+ * token is needed.
253
+ */
254
+ export type ClientAuthResult = ClientAuthState | UnauthState;
255
+ /**
256
+ * Options passed to the SDK middleware factory (`nextgenMiddleware` in
257
+ * sdk-next, `createNextgenMiddleware` in sdk-nuxt).
258
+ */
259
+ export type NextgenMiddlewareOptions = {
260
+ /**
261
+ * Full URL of the Zitadel auth backend.
262
+ * @default process.env.ZITADEL_URL ?? "http://localhost:8080"
263
+ */
264
+ url?: string;
265
+ /**
266
+ * The project this app signs its users in to. A session counts as signed in
267
+ * only when it belongs to this project. A session of any other project on the
268
+ * same server (a second app, or the Console, whose cookie the browser sends
269
+ * to every port of a shared host) reads as signed out, and its cookie is left
270
+ * alone.
271
+ *
272
+ * The app keeps its session in a cookie of its own,
273
+ * `__nextgen_session_<projectId>` ({@link sessionCookieName}), so signing in
274
+ * here neither replaces nor reads the session of another app or of the
275
+ * Console on the same host (ADR 073).
276
+ *
277
+ * An opaque session token is checked by the backend
278
+ * (`GET /sessions/me?project_id=…`) and against the project its answer names;
279
+ * a JWT must name the project in its `aud` claim.
280
+ *
281
+ * Required: with no project, no session counts as signed in, and the proxy
282
+ * passes the server's session cookie through unchanged. Each SDK falls back
283
+ * to the `ZITADEL_PROJECT_ID` environment variable, then to its framework's
284
+ * public project id variable, which `zitadel setup` writes to `.env.local`.
285
+ */
286
+ projectId?: string;
287
+ /**
288
+ * URL path prefix that is reverse-proxied to the auth backend.
289
+ *
290
+ * Sign-in with an external provider currently requires the default: the
291
+ * provider returns to `/__nextgen/idp/{slug}/callback`, which is proxied only under
292
+ * the default prefix.
293
+ * @default "/__nextgen"
294
+ */
295
+ proxyPath?: string;
296
+ /**
297
+ * Pathnames that require a valid session. Requests to these routes without
298
+ * a valid token are redirected to {@link loginPath}.
299
+ * Entries ending with `*` match any sub-path (e.g. `"/admin*"`).
300
+ *
301
+ * When the backend cannot say whether a session cookie is live (it answers
302
+ * `5xx`, does not answer in time, or cannot be reached), the route answers
303
+ * `503 Service Unavailable` instead: the user may well be signed in, and the
304
+ * login page would tell them they are not.
305
+ * @default []
306
+ */
307
+ protectedRoutes?: string[];
308
+ /**
309
+ * Pathnames that are completely skipped by the middleware — no JWT
310
+ * verification, no protection check, no header tunnelling.
311
+ * Useful for webhooks, health-check endpoints, or any route where
312
+ * running auth logic is undesirable.
313
+ * Entries ending with `*` match any sub-path (e.g. `"/public*"`).
314
+ * @default []
315
+ */
316
+ ignoredRoutes?: string[];
317
+ /**
318
+ * Pathname to redirect unauthenticated users to.
319
+ * A `next` query parameter carries the requested path and query string,
320
+ * where `<zitadel-login>` returns after sign-in.
321
+ * @default "/login"
322
+ */
323
+ loginPath?: string;
324
+ /**
325
+ * Reserved for future use. Local PEM public key for offline JWT verification.
326
+ */
327
+ jwtKey?: string;
328
+ /**
329
+ * Restrict accepted JWT `alg` header values. Tokens whose algorithm is not
330
+ * in this list are rejected before JWKS is fetched.
331
+ * @default ["RS256", "ES256"]
332
+ */
333
+ allowedAlgorithms?: string[];
334
+ /**
335
+ * Clock skew tolerance in milliseconds applied to `exp`, `nbf`, and `iat`
336
+ * claim validation.
337
+ * @default 5000
338
+ */
339
+ clockSkewMs?: number;
340
+ /**
341
+ * Expected value(s) of the `aud` claim. When omitted, audience is not
342
+ * validated. Pass a single string or an array when a token may carry
343
+ * multiple audiences.
344
+ */
345
+ audience?: string | string[];
346
+ /**
347
+ * Accepted values for the JWT header `typ` claim (case-insensitive).
348
+ * Tokens whose `typ` is not in this list are rejected.
349
+ * Set to `[]` to disable token-type checking entirely.
350
+ * @default ["JWT", "at+JWT"]
351
+ */
352
+ allowedTokenTypes?: string[];
353
+ /**
354
+ * Timeout in milliseconds for JWKS endpoint requests.
355
+ * Requests that do not complete within this window are aborted and the
356
+ * token is rejected, treating the request as unauthenticated.
357
+ * @default 5000
358
+ */
359
+ jwksTimeoutMs?: number;
360
+ /**
361
+ * Timeout in milliseconds for upstream proxy requests.
362
+ * Requests that do not complete within this window are aborted with a
363
+ * network error. Defaults to 5 000 ms.
364
+ * @default 5000
365
+ */
366
+ proxyTimeoutMs?: number;
367
+ /**
368
+ * Timeout in milliseconds for opaque (non-JWT) session token validation
369
+ * via the backend's `GET /sessions/me` endpoint. A token the backend does
370
+ * not answer for within this window is not refused: the request counts as
371
+ * signed out, its session cookie is kept, and a protected route answers
372
+ * `503` (see {@link protectedRoutes}).
373
+ * @default 5000
374
+ */
375
+ opaqueTokenTimeoutMs?: number;
376
+ };
377
+ /**
378
+ * What the backend reports for an opaque (non-JWT) session token.
379
+ *
380
+ * `userId` is `null` when the session names no user of this app's project.
381
+ * That is an anonymous session — `CreateSession` sets the session cookie
382
+ * before any user factor is verified, so an abandoned or in-progress sign-in
383
+ * holds a live session that names no user — or a live session of another
384
+ * project. Such a session is **not** signed in, but it is not dead either, so
385
+ * callers keep its cookie instead of clearing it: an anonymous sign-in may
386
+ * still complete, and the other project's session belongs to the app that
387
+ * signed it in.
388
+ */
389
+ export interface OpaqueSession {
390
+ readonly userId: string | null;
391
+ readonly identifier: string | null;
392
+ readonly identifierProperty: string | null;
393
+ readonly display: string | null;
394
+ /** When the session expires, as the backend reports it, or `null`. */
395
+ readonly expiresAt: string | null;
396
+ }
397
+ /**
398
+ * The error code the backend answers a session read with when the session
399
+ * belongs to another project than the one the read names.
400
+ */
401
+ export declare const SESSION_PROJECT_MISMATCH = "sess.project_mismatch";
402
+ /**
403
+ * The backend's verdict on an opaque session token, from
404
+ * {@link checkOpaqueSessionToken}.
405
+ *
406
+ * - `live`: the backend holds a live session for the token. Its `userId` is
407
+ * `null` when the session names no user of this app's project: an anonymous
408
+ * one, or one of another project (see {@link OpaqueSession}).
409
+ * - `refused`: the backend judged the token and refused it (`401`), or found
410
+ * the session it names gone (`404` with the `sess.not_found` code). Only
411
+ * this verdict makes the cookie dead.
412
+ * - `undecided`: the backend gave no verdict: a `5xx` or any other answer, no
413
+ * answer in time, a network error, or a body that cannot be read. The
414
+ * backend answers `500`, never `401`, when it cannot decide (its token or
415
+ * key store failed), so that an outage does not sign its users out. The
416
+ * request counts as signed out, but the cookie is kept.
417
+ */
418
+ export type OpaqueSessionCheck = {
419
+ readonly verdict: "live";
420
+ readonly session: OpaqueSession;
421
+ } | {
422
+ readonly verdict: "refused";
423
+ } | {
424
+ readonly verdict: "undecided";
425
+ };
426
+ /**
427
+ * Asks the backend (`GET {issuerUrl}/sessions/me?project_id={projectId}`)
428
+ * whether an opaque session token names a live session in this app's project,
429
+ * telling a refusal apart from no verdict.
430
+ *
431
+ * A live session of another project comes back with no user, like an
432
+ * anonymous one: it signs no one in here, and its cookie is not dead. The
433
+ * backend refuses such a session itself (`403 sess.project_mismatch`); the
434
+ * project its answer names is checked as well, because a backend that predates
435
+ * the parameter answers for any project.
436
+ *
437
+ * A `404` is a refusal only with the `sess.not_found` code. Without it, the
438
+ * answer came from something in front of the backend (a proxy, a `url` with
439
+ * the wrong path), which judged nothing.
440
+ *
441
+ * When there is no verdict, it logs why: the request it was asked for then
442
+ * renders signed out, or answers `503` on a protected route, and nothing else
443
+ * would say what went wrong.
444
+ *
445
+ * @param token - The opaque session token from the app's session cookie.
446
+ * @param issuerUrl - Base URL of the auth backend.
447
+ * @param timeoutMs - How long to wait for the backend before giving up.
448
+ * @param projectId - The project this app signs its users in to. Without one,
449
+ * no session can be shown to belong here: the backend is not asked, and the
450
+ * session reads as live with no user, so its cookie is kept.
451
+ * @returns The backend's {@link OpaqueSessionCheck | verdict}.
452
+ */
453
+ export declare function checkOpaqueSessionToken(token: string, issuerUrl: string, timeoutMs: number, projectId: string): Promise<OpaqueSessionCheck>;
454
+ /**
455
+ * Forgets which session tokens the backend placed in another project
456
+ * ({@link checkOpaqueSessionToken} remembers them for a while). For tests that
457
+ * answer for the same token differently.
458
+ */
459
+ export declare function clearSessionCheckCache(): void;
460
+ /**
461
+ * Validates an opaque session token by asking the backend
462
+ * (`GET {issuerUrl}/sessions/me?project_id={projectId}`) whether the session is
463
+ * live in this app's project.
464
+ *
465
+ * `null` folds a refusal and no verdict together, so it says whether the
466
+ * request is signed in, not whether the cookie is dead: clearing the cookie on
467
+ * `null` signs users out whenever the backend fails. To decide that, use
468
+ * {@link checkOpaqueSessionToken}.
469
+ *
470
+ * @param token - The opaque session token from the app's session cookie.
471
+ * @param issuerUrl - Base URL of the auth backend.
472
+ * @param timeoutMs - How long to wait for the backend before giving up.
473
+ * @param projectId - The project this app signs its users in to.
474
+ * @returns The live session — with a `null` `userId` when it names no user of
475
+ * this project — or `null` when the backend did not confirm one: it refused
476
+ * the token, or gave no verdict.
477
+ */
478
+ export declare function validateOpaqueSessionToken(token: string, issuerUrl: string, timeoutMs: number, projectId: string): Promise<OpaqueSession | null>;
479
+ /**
480
+ * Whether a verified JWT names `projectId` in its `aud` claim, the one place a
481
+ * self-contained token can say which project it was issued for.
482
+ */
483
+ export declare function jwtNamesProject(payload: JwtPayload, projectId: string): boolean;
484
+ /**
485
+ * What a request carries that decides its session. The request URL and the
486
+ * forwarded headers decide whether a cookie needs `Secure`.
487
+ */
488
+ export interface SessionRequest extends BrowserRequest {
489
+ /**
490
+ * The `Authorization: Bearer` token, or `null`. It takes precedence: when it
491
+ * signs a user of this project in, the cookie is not consulted.
492
+ */
493
+ readonly bearer: string | null;
494
+ /** Reads one of the request's cookies by name. */
495
+ readonly cookie: (name: string) => string | null | undefined;
496
+ }
497
+ /** The settings {@link resolveSession} judges a request's session by. */
498
+ export interface ResolveSessionOptions {
499
+ readonly url: string;
500
+ /** The project this app signs its users in to; see {@link NextgenMiddlewareOptions.projectId}. */
501
+ readonly projectId: string | undefined;
502
+ readonly allowedAlgorithms: readonly string[] | undefined;
503
+ readonly clockSkewMs: number;
504
+ readonly audience: string | readonly string[] | undefined;
505
+ readonly allowedTokenTypes: readonly string[];
506
+ readonly jwksTimeoutMs: number | undefined;
507
+ readonly opaqueTokenTimeoutMs: number;
508
+ }
509
+ /** A request's session, as {@link resolveSession} judged it. */
510
+ export interface ResolvedSession {
511
+ readonly auth: AuthResult;
512
+ /**
513
+ * Whether the backend gave no verdict on the session cookie
514
+ * ({@link OpaqueSessionCheck}). The request counts as signed out and the
515
+ * cookie is kept; a protected route answers `503` rather than send a user
516
+ * who may well be signed in to the login page.
517
+ */
518
+ readonly undecided: boolean;
519
+ /**
520
+ * The session cookies the response sets, with {@link SESSION_COOKIE_CACHE_CONTROL}
521
+ * when there are any. A cookie the backend refused is cleared so the browser
522
+ * stops sending it; a session of this project found in the server's cookie is
523
+ * copied to the project's own, and the server's cookie keeps it, since the
524
+ * Console may share the project. No other cookie is
525
+ * touched: a session that is live but signs no one in here (an anonymous
526
+ * one, or one of another project) keeps its cookie, and so does one the
527
+ * backend gave no verdict on, and one behind a Bearer token that signed the
528
+ * request in, which was never judged.
529
+ */
530
+ readonly cookies: readonly SessionCookie[];
531
+ }
532
+ /**
533
+ * Judges which user, if any, a request is signed in as in this app's project.
534
+ * The one implementation behind every server SDK's middleware, so they cannot
535
+ * drift on who counts as signed in.
536
+ *
537
+ * - A Bearer token is judged first, as a JWT. When it signs a user of this
538
+ * project in, that is the answer and the cookie is not judged; otherwise the
539
+ * cookie speaks for itself.
540
+ * - The session cookie is the project's own, else the server's. A session of
541
+ * this project found in the server's cookie is copied to the project's own.
542
+ * - A JWT-shaped session cookie signs its `sub` in when it verifies and its
543
+ * `aud` names the project. It never reaches the backend, which does not
544
+ * re-check JWT claims.
545
+ * - Any other session cookie is checked by the backend
546
+ * ({@link checkOpaqueSessionToken}) and signs its user in when the session
547
+ * is live in this project.
548
+ * - Only a cookie that held a dead session is cleared: one the backend refused,
549
+ * or a JWT that fails verification or names no user of this project. When
550
+ * the backend gives no verdict, the result says so
551
+ * ({@link ResolvedSession.undecided}) and the cookie stays.
552
+ * - Without a project nothing signs in, and no cookie is touched.
553
+ */
554
+ export declare function resolveSession(request: SessionRequest, options: ResolveSessionOptions): Promise<ResolvedSession>;
555
+ //#endregion
556
+ //# sourceMappingURL=middleware.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.mts","names":[],"sources":["../src/middleware.ts"],"mappings":";;;;;;;qBAoBa,YAAY;;;;;;qBA2BZ,kBAAkB;;;;;;;;;;;;;;wBAiBf,cAAc,kBAAkB;;;;;;;;;;;;;;;;;;;wBA8ChC,iBAAiB,mBAAmB,qBAAqB,MAAM;;;;;;;;;;;;;wBA+B/D,sBAAsB,gBAAgB;;;;;;;;;;;;;;;;;;;;wBAgEtC,sBAAsB,UAAU,SAAS,mBAAmB;;qBAsB/D;;;;;;;;;;;wBAeG,kBAAkB;;;;;iBAoBjB;WACN;WACA;;WAEA;WACA;;;;;;wBAOK,uBAAuB,QAAQ;;;;;;qBAiBlC;;iBAGI;;WAEN,qBAAqB;;WAErB;;WAEA;;;;;;iBAiGM,uBAAuB;WAC7B;;WAEA;;WAEA;;WAEA;;;iBAIM;;WAEN;;WAEA;;;iBAIM;WACN;WACA,MAAM,eAAe;;;iBAIf;;WAEN;;;;;;EAMT,UAAU,UAAU,WAAW,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAgCzB,oBACd,SAAS,gBACT,+BACA,UAAU,gBACT;;;;;;YAmJS;;EAEV;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA;;;YAIU;EAAc;EAAuB,SAAS;;;YAG9C;EAAgB;EAAwB;;;YAGxC,aAAa,YAAY;;;;;;;YAQzB;;EAEV;;EAEA;;EAEA;;EAEA;;;YAIU;EAAoB;EAAuB,SAAS;;;;;;;;YAQpD,mBAAmB,kBAAkB;;;;;YAMrC;;;;;EAKV;;;;;;;;;;;;;;;;;;;;;;EAuBA;;;;;;;;;EAUA;;;;;;;;;;;;EAaA;;;;;;;;;EAUA;;;;;;;EAQA;;;;EAKA;;;;;;EAOA;;;;;;EAOA;;;;;;EAOA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;;;EAUA;;;;;;;;;;;;;;iBAiBe;WACN;WACA;WACA;WACA;;WAEA;;;;;;qBAOE;;;;;;;;;;;;;;;;;YAuDD;WACG;WAA0B,SAAS;;WACnC;;WACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAgCO,wBACpB,eACA,mBACA,mBACA,oBACC,QAAQ;;;;;;wBA2FK;;;;;;;;;;;;;;;;;;;wBAsBM,2BACpB,eACA,mBACA,mBACA,oBACC,QAAQ;;;;;wBAsCK,gBAAgB,SAAS,YAAY;;;;;iBAWpC,uBAAuB;;;;;WAK7B;;WAEA,SAAS;;;iBAIH;WACN;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;;;iBAIM;WACN,MAAM;;;;;;;WAON;;;;;;;;;;;;WAYA,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;wBAsHP,eACpB,SAAS,gBACT,SAAS,wBACR,QAAQ"}