@zitadel/sdk-core 1.0.0-alpha.25 → 1.0.0-alpha.28
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.
- package/README.md +58 -3
- package/dist/index.d.mts +23 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +40 -0
- package/dist/index.mjs.map +1 -0
- package/dist/{jwt.d.ts → jwt.d.mts} +97 -83
- package/dist/jwt.d.mts.map +1 -0
- package/dist/jwt.mjs +465 -0
- package/dist/jwt.mjs.map +1 -0
- package/dist/middleware.d.mts +556 -0
- package/dist/middleware.d.mts.map +1 -0
- package/dist/middleware.mjs +764 -0
- package/dist/middleware.mjs.map +1 -0
- package/dist/types.d.mts +301 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +78 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +20 -13
- package/dist/index.d.ts +0 -22
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -42
- package/dist/jwt.d.ts.map +0 -1
- package/dist/jwt.js +0 -415
- package/dist/middleware.d.ts +0 -206
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -131
- package/dist/types.d.ts +0 -226
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -27
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ building your own middleware or need the types without a framework wrapper.
|
|
|
14
14
|
| `@zitadel/sdk-core` | The middleware, JWT, and runtime-resolution exports in one namespace. The `/types` widget contract is **not** re-exported from the root — import it from `@zitadel/sdk-core/types` |
|
|
15
15
|
| `@zitadel/sdk-core/types` | The SPA widget contract (`ZitadelFlowStepDetail` and the other widget event/config/handler/props types the SPA SDKs re-export) |
|
|
16
16
|
| `@zitadel/sdk-core/jwt` | `verifyJwt`, `decodeJwt`, `isJwtShaped`, `JWKS_TTL_MS` |
|
|
17
|
-
| `@zitadel/sdk-core/middleware` | The middleware layer: `NextgenSession`, `AuthResult`, `NextgenMiddlewareOptions`, route matching (`matchesRoutes`), response-header filtering (`filterResponseHeaders`, `HOP_BY_HOP`) |
|
|
17
|
+
| `@zitadel/sdk-core/middleware` | The middleware layer: `NextgenSession`, `AuthResult`, `NextgenMiddlewareOptions`, route matching (`matchesRoutes`), response-header filtering (`filterResponseHeaders`, `HOP_BY_HOP`), session judgement (`resolveSession`, the opaque session check `checkOpaqueSessionToken`), and the session cookie per project (`sessionCookieName`, `proxySessionCookies`, `serializeSessionCookie`) |
|
|
18
18
|
|
|
19
19
|
It also exports `resolveZitadelRuntime` / `resolveZitadelRuntimeEnv` and
|
|
20
20
|
`ZitadelRuntimeError` for resolving the runtime environment
|
|
@@ -26,7 +26,8 @@ This is the canonical description of the verification pipeline every SDK
|
|
|
26
26
|
middleware runs (`verifyJwt`):
|
|
27
27
|
|
|
28
28
|
1. The bearer token from the `Authorization` header is checked first; the
|
|
29
|
-
|
|
29
|
+
session cookie is the fallback (see
|
|
30
|
+
[The session cookie](#the-session-cookie)).
|
|
30
31
|
2. The JWT header is decoded to extract `kid` and `alg`.
|
|
31
32
|
3. Tokens with an `alg` not in `allowedAlgorithms` (`RS256`, `ES256` by
|
|
32
33
|
default) are rejected immediately — no JWKS fetch.
|
|
@@ -40,9 +41,63 @@ middleware runs (`verifyJwt`):
|
|
|
40
41
|
8. `exp` must be present and in the future (with `clockSkewMs` tolerance) —
|
|
41
42
|
tokens without an expiry are rejected.
|
|
42
43
|
9. `nbf` and `iat` are validated with `clockSkewMs` tolerance when present.
|
|
44
|
+
10. `aud` must name the app's project (`projectId`).
|
|
43
45
|
|
|
44
46
|
Opaque (non-JWT) session tokens are validated against the backend's
|
|
45
|
-
`GET /sessions/me
|
|
47
|
+
`GET /sessions/me?project_id=…` instead, bounded by `opaqueTokenTimeoutMs`
|
|
48
|
+
(`checkOpaqueSessionToken`).
|
|
49
|
+
|
|
50
|
+
A session belongs to one project, while a browser sends the session cookie
|
|
51
|
+
to every app on the same host, including the Console of a local server. So a
|
|
52
|
+
session counts as signed in only in the app's own project: `projectId`, which
|
|
53
|
+
each SDK otherwise reads from `ZITADEL_PROJECT_ID` (then its framework's public
|
|
54
|
+
project id variable, as `zitadel setup` writes both). The backend refuses an
|
|
55
|
+
opaque session of another project (`403 sess.project_mismatch`), and the SDK
|
|
56
|
+
checks the project the answer names as well. A session of another project
|
|
57
|
+
reads as signed out and keeps its cookie, since it is live in its own app.
|
|
58
|
+
With no project configured, no session counts as signed in.
|
|
59
|
+
|
|
60
|
+
Only the backend's refusal makes the session cookie dead, and only then does
|
|
61
|
+
the middleware clear it: a `401`, or a `404` with the `sess.not_found` code for
|
|
62
|
+
a session that is gone. A JWT session cookie is dead when it fails verification
|
|
63
|
+
or names no user of this project. Any other outcome (a `5xx`, no answer in
|
|
64
|
+
time, a network error) is not a refusal: the request counts as signed out, the
|
|
65
|
+
cookie stays, and a protected route answers `503 Service Unavailable` rather
|
|
66
|
+
than send a user who may well be signed in to the login page. A Bearer token
|
|
67
|
+
that signs no one in here leaves the cookie to be judged on its own.
|
|
68
|
+
|
|
69
|
+
`resolveSession` is the one implementation of these rules; every server SDK's
|
|
70
|
+
middleware calls it.
|
|
71
|
+
|
|
72
|
+
## The session cookie
|
|
73
|
+
|
|
74
|
+
The server sets and reads one session cookie, `__nextgen_session`. A browser
|
|
75
|
+
shares a host's cookies across every port, so under that name alone the Console
|
|
76
|
+
of a local server and every app on `localhost` would keep their sessions in one
|
|
77
|
+
slot and sign each other out. An app with a server SDK therefore keeps its
|
|
78
|
+
session in a cookie of its own, `sessionCookieName(projectId)`:
|
|
79
|
+
`__nextgen_session_<projectId>` (ADR 073).
|
|
80
|
+
|
|
81
|
+
- **The proxy** (`proxySessionCookies`) renames the server's `Set-Cookie` to the
|
|
82
|
+
app's name on the way to the browser, and on the way to the server sends the
|
|
83
|
+
app's cookie under the server's name. No other session cookie, the
|
|
84
|
+
Console's or another app's, goes up.
|
|
85
|
+
- **The middleware** (`resolveSession`) reads the app's cookie, and clears only
|
|
86
|
+
that cookie when the backend refuses its session.
|
|
87
|
+
- **A session kept under the server's name** before the change, by this app or,
|
|
88
|
+
on `localhost`, by the Console, is read only where the server holds it to the
|
|
89
|
+
app's project: by the middleware, and by the proxy for the session probe
|
|
90
|
+
`GET /sessions/me?project_id=…`. A session that signs a user of this project
|
|
91
|
+
in is copied to the app's cookie, and the old cookie stays: the Console may
|
|
92
|
+
share the project. When the app next signs a user in (`POST /sessions/exchange`)
|
|
93
|
+
or out (`DELETE /sessions/me`), the proxy retires it: cleared when it holds a
|
|
94
|
+
session of this project (and on sign-out that session is ended), or a dead
|
|
95
|
+
one; left when it holds another project's.
|
|
96
|
+
- **Every response that sets a session cookie** sends
|
|
97
|
+
`Cache-Control: private, no-store` (`SESSION_COOKIE_CACHE_CONTROL`), and
|
|
98
|
+
`Secure` follows the server's rule on the scheme and host a proxy in front of
|
|
99
|
+
the app forwarded (`X-Forwarded-Proto`, `X-Forwarded-Host`).
|
|
100
|
+
- **Without a project**, the proxy passes the cookies through unchanged.
|
|
46
101
|
|
|
47
102
|
Framework-specific steps (header tunnelling, `auth()` re-verification) are
|
|
48
103
|
documented in each framework SDK's README on top of this pipeline.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DecodedJwt, JWKS_MIN_REFRESH_MS, JWKS_TTL_MS, JwtHeader, JwtPayload, VerifyJwtOptions, base64UrlDecode, clearJwksCache, decodeJwt, isJwtShaped, verifyJwt } from "./jwt.mjs";
|
|
2
|
+
import { AuthResult, AuthState, BrowserRequest, HOP_BY_HOP, INTERNAL_HEADERS, NextgenMiddlewareOptions, NextgenSession, OpaqueSession, OpaqueSessionCheck, ProxiedAnswer, ProxiedRequest, ProxiedSessionCookies, ProxyUpstream, ResolveSessionOptions, ResolvedSession, SERVER_SESSION_COOKIE, SESSION_COOKIE_CACHE_CONTROL, SESSION_PROJECT_MISMATCH, SessionCookie, SessionRequest, UnauthState, checkOpaqueSessionToken, clearSessionCheckCache, filterResponseHeaders, jwtNamesProject, matchesRoutes, proxySessionCookies, requiresProjectSecret, resolveSession, serializeSessionCookie, sessionCookieName, validateOpaqueSessionToken } from "./middleware.mjs";
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
export type ZitadelEnvironment = "development" | "preview" | "production";
|
|
5
|
+
export type ZitadelRuntime = {
|
|
6
|
+
projectId: string;
|
|
7
|
+
environment: ZitadelEnvironment;
|
|
8
|
+
issuer?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ZitadelRuntimeInput = {
|
|
11
|
+
projectId?: string;
|
|
12
|
+
environment?: ZitadelEnvironment;
|
|
13
|
+
issuer?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare class ZitadelRuntimeError extends Error {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
constructor(code: string, message: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function resolveZitadelRuntimeEnv(env?: Record<string, string | undefined>): ZitadelRuntime;
|
|
20
|
+
export declare function resolveZitadelRuntime(input: ZitadelRuntimeInput): ZitadelRuntime;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { type AuthResult, type AuthState, type BrowserRequest, type DecodedJwt, HOP_BY_HOP, INTERNAL_HEADERS, JWKS_MIN_REFRESH_MS, JWKS_TTL_MS, type JwtHeader, type JwtPayload, type NextgenMiddlewareOptions, type NextgenSession, type OpaqueSession, type OpaqueSessionCheck, type ProxiedAnswer, type ProxiedRequest, type ProxiedSessionCookies, type ProxyUpstream, type ResolveSessionOptions, type ResolvedSession, SERVER_SESSION_COOKIE, SESSION_COOKIE_CACHE_CONTROL, SESSION_PROJECT_MISMATCH, type SessionCookie, type SessionRequest, type UnauthState, type VerifyJwtOptions, base64UrlDecode, checkOpaqueSessionToken, clearJwksCache, clearSessionCheckCache, decodeJwt, filterResponseHeaders, isJwtShaped, jwtNamesProject, matchesRoutes, proxySessionCookies, requiresProjectSecret, resolveSession, serializeSessionCookie, sessionCookieName, validateOpaqueSessionToken, verifyJwt };
|
|
23
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;YAiDY;YAEA;EACV;EACA,aAAa;EACb;;YAGU;EACV;EACA,cAAc;EACd;;qBAGW,4BAA4B;WAC9B;EAEG,YAAA,cAAc;;wBAOZ,yBACd,MAAK,qCACJ;wBAQa,sBAAsB,OAAO,sBAAsB"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { JWKS_MIN_REFRESH_MS, JWKS_TTL_MS, base64UrlDecode, clearJwksCache, decodeJwt, isJwtShaped, verifyJwt } from "./jwt.mjs";
|
|
2
|
+
import { HOP_BY_HOP, INTERNAL_HEADERS, SERVER_SESSION_COOKIE, SESSION_COOKIE_CACHE_CONTROL, SESSION_PROJECT_MISMATCH, checkOpaqueSessionToken, clearSessionCheckCache, filterResponseHeaders, jwtNamesProject, matchesRoutes, proxySessionCookies, requiresProjectSecret, resolveSession, serializeSessionCookie, sessionCookieName, validateOpaqueSessionToken } from "./middleware.mjs";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
var ZitadelRuntimeError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "ZitadelRuntimeError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function resolveZitadelRuntimeEnv(env = currentEnv()) {
|
|
13
|
+
return resolveZitadelRuntime({
|
|
14
|
+
projectId: env.NEXT_PUBLIC_ZITADEL_PROJECT_ID ?? env.ZITADEL_PROJECT_ID,
|
|
15
|
+
environment: parseEnvironment(env.NEXT_PUBLIC_ZITADEL_ENVIRONMENT ?? env.ZITADEL_ENVIRONMENT),
|
|
16
|
+
issuer: env.NEXT_PUBLIC_ZITADEL_ISSUER ?? env.ZITADEL_ISSUER
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function resolveZitadelRuntime(input) {
|
|
20
|
+
const environment = input.environment ?? "development";
|
|
21
|
+
if (!input.projectId) throw new ZitadelRuntimeError("E_ZITADEL_CONFIG", "ZITADEL_PROJECT_ID is required for Zitadel runtime.");
|
|
22
|
+
if (environment === "production" && !input.issuer) throw new ZitadelRuntimeError("E_ZITADEL_CONFIG", "ZITADEL_ISSUER is required in production.");
|
|
23
|
+
return {
|
|
24
|
+
projectId: input.projectId,
|
|
25
|
+
environment,
|
|
26
|
+
issuer: input.issuer
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function parseEnvironment(value) {
|
|
30
|
+
if (!value || value === "development") return "development";
|
|
31
|
+
if (value === "preview" || value === "production") return value;
|
|
32
|
+
throw new ZitadelRuntimeError("E_ZITADEL_CONFIG", `Unsupported ZITADEL_ENVIRONMENT "${value}".`);
|
|
33
|
+
}
|
|
34
|
+
function currentEnv() {
|
|
35
|
+
return globalThis.process?.env ?? {};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { HOP_BY_HOP, INTERNAL_HEADERS, JWKS_MIN_REFRESH_MS, JWKS_TTL_MS, SERVER_SESSION_COOKIE, SESSION_COOKIE_CACHE_CONTROL, SESSION_PROJECT_MISMATCH, ZitadelRuntimeError, base64UrlDecode, checkOpaqueSessionToken, clearJwksCache, clearSessionCheckCache, decodeJwt, filterResponseHeaders, isJwtShaped, jwtNamesProject, matchesRoutes, proxySessionCookies, requiresProjectSecret, resolveSession, resolveZitadelRuntime, resolveZitadelRuntimeEnv, serializeSessionCookie, sessionCookieName, validateOpaqueSessionToken, verifyJwt };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["scope"],"sources":["../src/index.ts"],"sourcesContent":["export type {\n NextgenSession,\n AuthState,\n UnauthState,\n AuthResult,\n NextgenMiddlewareOptions,\n} from \"./middleware.js\";\nexport {\n HOP_BY_HOP,\n INTERNAL_HEADERS,\n matchesRoutes,\n filterResponseHeaders,\n requiresProjectSecret,\n checkOpaqueSessionToken,\n validateOpaqueSessionToken,\n jwtNamesProject,\n resolveSession,\n SESSION_PROJECT_MISMATCH,\n SERVER_SESSION_COOKIE,\n sessionCookieName,\n serializeSessionCookie,\n proxySessionCookies,\n clearSessionCheckCache,\n SESSION_COOKIE_CACHE_CONTROL,\n} from \"./middleware.js\";\nexport type {\n OpaqueSession,\n OpaqueSessionCheck,\n SessionRequest,\n SessionCookie,\n ResolveSessionOptions,\n ResolvedSession,\n ProxiedRequest,\n ProxiedAnswer,\n ProxiedSessionCookies,\n ProxyUpstream,\n BrowserRequest,\n} from \"./middleware.js\";\nexport {\n verifyJwt,\n decodeJwt,\n isJwtShaped,\n base64UrlDecode,\n JWKS_TTL_MS,\n JWKS_MIN_REFRESH_MS,\n clearJwksCache,\n} from \"./jwt.js\";\nexport type { JwtPayload, JwtHeader, DecodedJwt, VerifyJwtOptions } from \"./jwt.js\";\n\nexport type ZitadelEnvironment = \"development\" | \"preview\" | \"production\";\n\nexport type ZitadelRuntime = {\n projectId: string;\n environment: ZitadelEnvironment;\n issuer?: string;\n};\n\nexport type ZitadelRuntimeInput = {\n projectId?: string;\n environment?: ZitadelEnvironment;\n issuer?: string;\n};\n\nexport class ZitadelRuntimeError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"ZitadelRuntimeError\";\n this.code = code;\n }\n}\n\nexport function resolveZitadelRuntimeEnv(\n env: Record<string, string | undefined> = currentEnv(),\n): ZitadelRuntime {\n return resolveZitadelRuntime({\n projectId: env.NEXT_PUBLIC_ZITADEL_PROJECT_ID ?? env.ZITADEL_PROJECT_ID,\n environment: parseEnvironment(env.NEXT_PUBLIC_ZITADEL_ENVIRONMENT ?? env.ZITADEL_ENVIRONMENT),\n issuer: env.NEXT_PUBLIC_ZITADEL_ISSUER ?? env.ZITADEL_ISSUER,\n });\n}\n\nexport function resolveZitadelRuntime(input: ZitadelRuntimeInput): ZitadelRuntime {\n const environment = input.environment ?? \"development\";\n if (!input.projectId) {\n throw new ZitadelRuntimeError(\n \"E_ZITADEL_CONFIG\",\n \"ZITADEL_PROJECT_ID is required for Zitadel runtime.\",\n );\n }\n if (environment === \"production\" && !input.issuer) {\n throw new ZitadelRuntimeError(\"E_ZITADEL_CONFIG\", \"ZITADEL_ISSUER is required in production.\");\n }\n return {\n projectId: input.projectId,\n environment,\n issuer: input.issuer,\n };\n}\n\nfunction parseEnvironment(value: string | undefined): ZitadelEnvironment {\n if (!value || value === \"development\") return \"development\";\n if (value === \"preview\" || value === \"production\") return value;\n throw new ZitadelRuntimeError(\"E_ZITADEL_CONFIG\", `Unsupported ZITADEL_ENVIRONMENT \"${value}\".`);\n}\n\nfunction currentEnv(): Record<string, string | undefined> {\n const scope = globalThis as { process?: { env?: Record<string, string | undefined> } };\n return scope.process?.env ?? {};\n}\n"],"mappings":";;;AA+DA,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CAEA,YAAY,MAAc,SAAiB;EACzC,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,yBACd,MAA0C,WAAW,GACrC;CAChB,OAAO,sBAAsB;EAC3B,WAAW,IAAI,kCAAkC,IAAI;EACrD,aAAa,iBAAiB,IAAI,mCAAmC,IAAI,mBAAmB;EAC5F,QAAQ,IAAI,8BAA8B,IAAI;CAChD,CAAC;AACH;AAEA,SAAgB,sBAAsB,OAA4C;CAChF,MAAM,cAAc,MAAM,eAAe;CACzC,IAAI,CAAC,MAAM,WACT,MAAM,IAAI,oBACR,oBACA,qDACF;CAEF,IAAI,gBAAgB,gBAAgB,CAAC,MAAM,QACzC,MAAM,IAAI,oBAAoB,oBAAoB,2CAA2C;CAE/F,OAAO;EACL,WAAW,MAAM;EACjB;EACA,QAAQ,MAAM;CAChB;AACF;AAEA,SAAS,iBAAiB,OAA+C;CACvE,IAAI,CAAC,SAAS,UAAU,eAAe,OAAO;CAC9C,IAAI,UAAU,aAAa,UAAU,cAAc,OAAO;CAC1D,MAAM,IAAI,oBAAoB,oBAAoB,oCAAoC,MAAM,GAAG;AACjG;AAEA,SAAS,aAAiD;CAExD,OAAOA,WAAM,SAAS,OAAO,CAAC;AAChC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/jwt.d.ts
|
|
1
2
|
/**
|
|
2
3
|
* @file lib/jwt.ts
|
|
3
4
|
*
|
|
@@ -81,17 +82,16 @@
|
|
|
81
82
|
*
|
|
82
83
|
* ## JWKS caching
|
|
83
84
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* Each issuer's whole JWKS document is cached in-process, keyed by its JWKS URI,
|
|
86
|
+
* for {@link JWKS_TTL_MS} milliseconds (default 5 minutes); keys are imported
|
|
87
|
+
* once and reused. Keying by the URI keeps two issuers that publish the same
|
|
88
|
+
* `kid` apart.
|
|
87
89
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* A cache miss — on first use or after TTL expiry — triggers a fresh fetch
|
|
94
|
-
* from `{issuerUrl}/auth/keys`.
|
|
90
|
+
* The document is fetched from `{issuerUrl}/auth/keys` on first use and after
|
|
91
|
+
* the TTL. A token naming a `kid` the cached document lacks refreshes it — how
|
|
92
|
+
* a rotated key is picked up — but no sooner than {@link JWKS_MIN_REFRESH_MS}
|
|
93
|
+
* after the last fetch. The `kid` header is attacker-controlled, so a per-`kid`
|
|
94
|
+
* cache would let any request with a made-up `kid` cost a JWKS fetch.
|
|
95
95
|
*
|
|
96
96
|
* > **Note (serverless):** the cache is module-level. In serverless environments
|
|
97
97
|
* > each cold start gets a fresh cache; warm instances share it across requests.
|
|
@@ -106,8 +106,15 @@
|
|
|
106
106
|
* The previous per-SDK copies (one using `atob`, one using `Buffer`) have
|
|
107
107
|
* been consolidated here.
|
|
108
108
|
*/
|
|
109
|
-
/** How long a cached JWKS
|
|
109
|
+
/** How long a cached JWKS document is considered fresh before re-fetching. */
|
|
110
110
|
export declare const JWKS_TTL_MS: number;
|
|
111
|
+
/**
|
|
112
|
+
* The least time between two JWKS fetches for one issuer. A token naming a
|
|
113
|
+
* `kid` the cached document lacks triggers a refresh — that is how a rotated
|
|
114
|
+
* key is picked up — but at most this often, so a stream of tokens with random
|
|
115
|
+
* `kid` values cannot turn every request into a JWKS fetch.
|
|
116
|
+
*/
|
|
117
|
+
export declare const JWKS_MIN_REFRESH_MS: number;
|
|
111
118
|
/**
|
|
112
119
|
* The subset of standard JWT payload claims this module reads or validates.
|
|
113
120
|
*
|
|
@@ -117,26 +124,26 @@ export declare const JWKS_TTL_MS: number;
|
|
|
117
124
|
* Refer to the module-level documentation for the full verification matrix.
|
|
118
125
|
*/
|
|
119
126
|
export interface JwtPayload {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
127
|
+
/** Issuer (`iss`) — identifies the auth server that created the token. */
|
|
128
|
+
readonly iss?: string;
|
|
129
|
+
/** Subject (`sub`) — the user's unique identifier. */
|
|
130
|
+
readonly sub?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Audience (`aud`) — the intended recipient(s).
|
|
133
|
+
* A single string or an array of strings per RFC 7519 §4.1.3.
|
|
134
|
+
*/
|
|
135
|
+
readonly aud?: string | readonly string[];
|
|
136
|
+
/** Expiration time (`exp`) — seconds since Unix epoch. */
|
|
137
|
+
readonly exp?: number;
|
|
138
|
+
/** Not before (`nbf`) — seconds since Unix epoch. */
|
|
139
|
+
readonly nbf?: number;
|
|
140
|
+
/** Issued at (`iat`) — seconds since Unix epoch. */
|
|
141
|
+
readonly iat?: number;
|
|
142
|
+
/** User's email address (custom claim). */
|
|
143
|
+
readonly email?: string;
|
|
144
|
+
/** User's display name (custom claim). */
|
|
145
|
+
readonly name?: string;
|
|
146
|
+
readonly [key: string]: unknown;
|
|
140
147
|
}
|
|
141
148
|
/**
|
|
142
149
|
* The typed representation of a JWT header.
|
|
@@ -145,13 +152,13 @@ export interface JwtPayload {
|
|
|
145
152
|
* header parameters are preserved under the index signature.
|
|
146
153
|
*/
|
|
147
154
|
export interface JwtHeader {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
/** Signing algorithm (`alg`) — e.g. `"RS256"`, `"ES256"`. */
|
|
156
|
+
readonly alg?: string;
|
|
157
|
+
/** Token type (`typ`) — e.g. `"JWT"`, `"at+JWT"`. */
|
|
158
|
+
readonly typ?: string;
|
|
159
|
+
/** Key ID (`kid`) — selects the matching public key from the JWKS. */
|
|
160
|
+
readonly kid?: string;
|
|
161
|
+
readonly [key: string]: unknown;
|
|
155
162
|
}
|
|
156
163
|
/**
|
|
157
164
|
* The decoded, unverified parts of a compact-serialised JWT.
|
|
@@ -161,8 +168,8 @@ export interface JwtHeader {
|
|
|
161
168
|
* raw data into the next step.
|
|
162
169
|
*/
|
|
163
170
|
export interface DecodedJwt {
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
readonly header: JwtHeader;
|
|
172
|
+
readonly payload: JwtPayload;
|
|
166
173
|
}
|
|
167
174
|
/**
|
|
168
175
|
* Options for {@link verifyJwt}.
|
|
@@ -170,49 +177,55 @@ export interface DecodedJwt {
|
|
|
170
177
|
* All fields are `readonly` — this object is never mutated after construction.
|
|
171
178
|
*/
|
|
172
179
|
export interface VerifyJwtOptions {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Base URL of the auth backend.
|
|
182
|
+
*
|
|
183
|
+
* Serves two purposes:
|
|
184
|
+
* 1. Determines the JWKS endpoint: `{issuerUrl}/auth/keys`
|
|
185
|
+
* 2. Is compared against the `iss` claim when present in the token.
|
|
186
|
+
*/
|
|
187
|
+
readonly issuerUrl: string;
|
|
188
|
+
/**
|
|
189
|
+
* Restrict accepted `alg` header values. Tokens whose algorithm is not in
|
|
190
|
+
* this list are rejected before the JWKS is fetched.
|
|
191
|
+
* When `undefined`, all algorithms are accepted (the middleware layer
|
|
192
|
+
* defaults to `["RS256", "ES256"]` before passing options here).
|
|
193
|
+
*/
|
|
194
|
+
readonly allowedAlgorithms?: readonly string[];
|
|
195
|
+
/**
|
|
196
|
+
* Clock-skew tolerance in milliseconds, applied symmetrically to `exp`,
|
|
197
|
+
* `nbf`, and `iat` checks to accommodate minor differences between clocks.
|
|
198
|
+
*/
|
|
199
|
+
readonly clockSkewMs: number;
|
|
200
|
+
/**
|
|
201
|
+
* Expected `aud` claim value(s). When `undefined`, audience is not
|
|
202
|
+
* validated. Provide a string or array when the auth server sets `aud`
|
|
203
|
+
* and you want to ensure the token was issued specifically for your app.
|
|
204
|
+
*/
|
|
205
|
+
readonly audience?: string | readonly string[];
|
|
206
|
+
/**
|
|
207
|
+
* Accepted `typ` header values (case-insensitive).
|
|
208
|
+
*
|
|
209
|
+
* Guards against using a refresh token or ID token where an access token
|
|
210
|
+
* is expected. Set to an empty array (`[]`) to disable this check.
|
|
211
|
+
*
|
|
212
|
+
* @default ["JWT", "at+JWT"]
|
|
213
|
+
*/
|
|
214
|
+
readonly allowedTokenTypes: readonly string[];
|
|
215
|
+
/**
|
|
216
|
+
* Timeout in milliseconds for JWKS endpoint requests.
|
|
217
|
+
* Requests that do not complete within this window are aborted and the
|
|
218
|
+
* token is rejected (function returns `null`).
|
|
219
|
+
* @default 5000
|
|
220
|
+
*/
|
|
221
|
+
readonly jwksTimeoutMs?: number;
|
|
215
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Forgets every cached JWKS document, so the next verification fetches the key
|
|
225
|
+
* set anew. Use it to drop a compromised key at once instead of waiting out
|
|
226
|
+
* {@link JWKS_TTL_MS}, and in tests that publish different keys per case.
|
|
227
|
+
*/
|
|
228
|
+
export declare function clearJwksCache(): void;
|
|
216
229
|
/**
|
|
217
230
|
* Decodes a Base64URL-encoded string into a `Uint8Array`.
|
|
218
231
|
*
|
|
@@ -279,4 +292,5 @@ export declare function isJwtShaped(token: string): boolean;
|
|
|
279
292
|
* @returns The verified {@link JwtPayload}, or `null` if verification fails.
|
|
280
293
|
*/
|
|
281
294
|
export declare function verifyJwt(token: string, opts: VerifyJwtOptions): Promise<JwtPayload | null>;
|
|
282
|
-
//#
|
|
295
|
+
//#endregion
|
|
296
|
+
//# sourceMappingURL=jwt.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.mts","names":[],"sources":["../src/jwt.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiHa;;;;;;;qBAQA;;;;;;;;;iBAYI;;WAEN;;WAEA;;;;;WAKA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;YACC;;;;;;;;iBASK;;WAEN;;WAEA;;WAEA;YACC;;;;;;;;;iBAUK;WACN,QAAQ;WACR,SAAS;;;;;;;iBAuBH;;;;;;;;WAQN;;;;;;;WAQA;;;;;WAMA;;;;;;WAOA;;;;;;;;;WAUA;;;;;;;WAQA;;;;;;;wBAkCK;;;;;;;;;;wBAqBA,gBAAgB,gBAAgB,WAAW;;;;;;;;;;;;;wBA2C3C,UAAU,gBAAgB;;;;;;;;;;;;;;;;;;wBAiC1B,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;wBA8ON,UAAU,eAAe,MAAM,mBAAmB,QAAQ"}
|