@browserlessapi/inferlo 0.7.0 → 0.8.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.
Files changed (78) hide show
  1. package/README.md +3 -2
  2. package/dist/app.d.ts +24 -0
  3. package/dist/app.js +336 -13
  4. package/dist/app.js.map +1 -1
  5. package/dist/authjs.d.ts +70 -0
  6. package/dist/authjs.js +94 -0
  7. package/dist/authjs.js.map +1 -0
  8. package/dist/better-auth.d.ts +89 -0
  9. package/dist/better-auth.js +75 -0
  10. package/dist/better-auth.js.map +1 -0
  11. package/dist/cli/main.js +1 -1
  12. package/dist/cli/main.js.map +1 -1
  13. package/dist/client-ip.d.ts +5 -0
  14. package/dist/client-ip.js +5 -0
  15. package/dist/client-ip.js.map +1 -1
  16. package/dist/cross-site.d.ts +14 -0
  17. package/dist/cross-site.js +56 -0
  18. package/dist/cross-site.js.map +1 -0
  19. package/dist/db/postgres.js +19 -5
  20. package/dist/db/postgres.js.map +1 -1
  21. package/dist/describe.d.ts +9 -2
  22. package/dist/describe.js +11 -1
  23. package/dist/describe.js.map +1 -1
  24. package/dist/disk-storage.d.ts +6 -0
  25. package/dist/disk-storage.js +96 -0
  26. package/dist/disk-storage.js.map +1 -0
  27. package/dist/files.d.ts +23 -0
  28. package/dist/files.js +168 -0
  29. package/dist/files.js.map +1 -0
  30. package/dist/index.d.ts +15 -4
  31. package/dist/index.js +6 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/jobs.d.ts +10 -4
  34. package/dist/jobs.js +7 -2
  35. package/dist/jobs.js.map +1 -1
  36. package/dist/jwt.d.ts +83 -0
  37. package/dist/jwt.js +312 -0
  38. package/dist/jwt.js.map +1 -0
  39. package/dist/model.js +6 -1
  40. package/dist/model.js.map +1 -1
  41. package/dist/oidc.d.ts +70 -0
  42. package/dist/oidc.js +379 -0
  43. package/dist/oidc.js.map +1 -0
  44. package/dist/openapi.js +17 -2
  45. package/dist/openapi.js.map +1 -1
  46. package/dist/passport.d.ts +54 -0
  47. package/dist/passport.js +68 -0
  48. package/dist/passport.js.map +1 -0
  49. package/dist/postgres-js.d.ts +39 -0
  50. package/dist/postgres-js.js +71 -0
  51. package/dist/postgres-js.js.map +1 -0
  52. package/dist/redis-sockets.d.ts +24 -0
  53. package/dist/redis-sockets.js +75 -0
  54. package/dist/redis-sockets.js.map +1 -0
  55. package/dist/route.d.ts +103 -2
  56. package/dist/route.js +27 -0
  57. package/dist/route.js.map +1 -1
  58. package/dist/schema.d.ts +21 -1
  59. package/dist/schema.js +70 -0
  60. package/dist/schema.js.map +1 -1
  61. package/dist/serve.d.ts +18 -0
  62. package/dist/serve.js +137 -2
  63. package/dist/serve.js.map +1 -1
  64. package/dist/sessions.js +35 -4
  65. package/dist/sessions.js.map +1 -1
  66. package/dist/sockets.d.ts +58 -0
  67. package/dist/sockets.js +111 -0
  68. package/dist/sockets.js.map +1 -0
  69. package/dist/storage.d.ts +47 -0
  70. package/dist/storage.js +123 -0
  71. package/dist/storage.js.map +1 -0
  72. package/dist/websocket.d.ts +81 -0
  73. package/dist/websocket.js +251 -0
  74. package/dist/websocket.js.map +1 -0
  75. package/llms.txt +1 -0
  76. package/package.json +43 -3
  77. package/templates/app/AGENTS.md +5 -3
  78. package/templates/app/test/notes.test.ts +2 -0
package/dist/oidc.js ADDED
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Single sign-on for browser apps with OpenID Connect (Okta, Microsoft Entra ID, Google, Auth0, Keycloak, Ping,
3
+ * OneLogin, JumpCloud, Authentik, Zitadel and any other OpenID provider): the authorization code flow with PKCE,
4
+ * state and nonce, ending in a sessions() cookie.
5
+ *
6
+ * export const session = sessions({ secret: settings.SESSION_SECRET, user: ({ userId }, { db }) => db.users.find(userId) });
7
+ * export const sso = oidc({
8
+ * issuer: "https://acme.okta.com",
9
+ * clientId: settings.OIDC_CLIENT_ID,
10
+ * clientSecret: settings.OIDC_CLIENT_SECRET,
11
+ * session,
12
+ * userId: async (claims, { db }) => (await db.users.findOne({ email: claims.email }))?.id ?? null,
13
+ * });
14
+ * createApp({ auth: session, routes: [notes, sso] });
15
+ *
16
+ * GET /auth/login (?returnTo=/path) sends the browser to the provider; GET /auth/callback checks what comes back
17
+ * (state, the code exchanged with the PKCE verifier, the ID token's signature, issuer, audience, expiry and nonce),
18
+ * asks userId(claims) which user signs in, starts the session and sends the browser back. POST /auth/logout ends the
19
+ * session and signs the user out at the provider too (RP-initiated logout). No runtime dependencies.
20
+ */
21
+ import { safeEqual } from "./auth.js";
22
+ import { InferloError } from "./errors.js";
23
+ import { ASYMMETRIC, createJwtVerifier, isLocal } from "./jwt.js";
24
+ import { route } from "./route.js";
25
+ const OPTIONS = ["issuer", "clientId", "clientSecret", "session", "userId", "redirectUri", "scopes", "basePath", "authorizationParams", "postLogoutRedirectUri"];
26
+ /** The parameters oidc() sets itself, which authorizationParams cannot change. */
27
+ const RESERVED_PARAMS = ["response_type", "client_id", "redirect_uri", "scope", "state", "nonce", "code_challenge", "code_challenge_method", "response_mode", "request", "request_uri"];
28
+ /** The prompt values a login link may ask for with ?prompt= (OpenID Connect Core and Initiating User Registration). */
29
+ const PROMPTS = ["none", "login", "consent", "select_account", "create"];
30
+ /** ID tokens up to this size are kept for signing out at the provider; larger ones would not fit in a cookie. */
31
+ const MAX_ID_TOKEN_COOKIE = 3500;
32
+ /** How long the ID token is kept for signing out (the longest a sessions() cookie can last). */
33
+ const ID_TOKEN_COOKIE_SECONDS = 400 * 86_400;
34
+ const FETCH_TIMEOUT = 5000;
35
+ const DISCOVERY_CACHE_MS = 600_000;
36
+ const PENDING_MS = 600_000;
37
+ const fix = 'Use oidc({ issuer: "https://login.example.com", clientId: settings.OIDC_CLIENT_ID, clientSecret: settings.OIDC_CLIENT_SECRET, session, userId: (claims, { db }) => ... }) with the issuer, client id and secret your identity provider shows for this app.';
38
+ function invalid(message, fixText = fix) {
39
+ return new InferloError("IL6047", message, { fix: fixText });
40
+ }
41
+ function failed(reason) {
42
+ return new InferloError("IL3009", `The sign-in could not be completed: ${reason}.`, {
43
+ status: 400,
44
+ fix: "Start the sign-in again from the app's login link, in the same browser. If it keeps failing, check the app's OpenID Connect settings (issuer, client id, redirect URI) at the provider.",
45
+ });
46
+ }
47
+ function unreachable(what, cause) {
48
+ return new InferloError("IL3010", `The identity provider could not be used: ${what}.`, {
49
+ status: 502,
50
+ fix: `Check that the issuer, client id and client secret are right and that the provider is reachable from the server (${cause instanceof Error ? cause.message : String(cause)}).`,
51
+ });
52
+ }
53
+ function httpsUrl(value, what) {
54
+ let url;
55
+ try {
56
+ url = new URL(String(value));
57
+ }
58
+ catch {
59
+ throw invalid(`oidc() ${what} must be a URL, got ${JSON.stringify(value)}.`);
60
+ }
61
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocal(url))) {
62
+ throw invalid(`oidc() ${what} must use https (plain http only on localhost), got ${JSON.stringify(value)}.`);
63
+ }
64
+ return url;
65
+ }
66
+ function base64url(bytes) {
67
+ let binary = "";
68
+ for (const byte of bytes)
69
+ binary += String.fromCharCode(byte);
70
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
71
+ }
72
+ function random() {
73
+ return base64url(crypto.getRandomValues(new Uint8Array(32)));
74
+ }
75
+ /** A path on this app to return to after the sign-in, or "/": never another site (open redirect). */
76
+ function safeReturnTo(value) {
77
+ if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.startsWith("/\\") || value.length > 2000)
78
+ return "/";
79
+ if (/[\u0000-\u001f\u007f\\]/.test(value))
80
+ return "/";
81
+ return value;
82
+ }
83
+ function readCookie(header, name) {
84
+ if (!header)
85
+ return undefined;
86
+ for (const part of header.split(";")) {
87
+ const index = part.indexOf("=");
88
+ if (index !== -1 && part.slice(0, index).trim() === name)
89
+ return part.slice(index + 1).trim();
90
+ }
91
+ return undefined;
92
+ }
93
+ /**
94
+ * The short-lived cookie that carries state, nonce and the PKCE verifier from login to callback ("inferlo_oidc"),
95
+ * and the one that keeps the ID token for signing out at the provider ("inferlo_oidc_id").
96
+ */
97
+ function cookieFor(url, base = "inferlo_oidc") {
98
+ const local = url.protocol === "http:" && isLocal(url);
99
+ // __Host- cookies cannot be set by subdomains or for other paths; browsers need Secure for them.
100
+ return local ? { name: base, attributes: "Path=/; HttpOnly; SameSite=Lax" } : { name: `__Host-${base}`, attributes: "Path=/; HttpOnly; Secure; SameSite=Lax" };
101
+ }
102
+ /** The iss claim of a JWT, without checking it (only to pick the provider an ID token came from). */
103
+ function issuerOf(token) {
104
+ try {
105
+ const payload = token.split(".")[1] ?? "";
106
+ const claims = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(payload.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
107
+ return typeof claims.iss === "string" ? claims.iss : undefined;
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ }
113
+ /**
114
+ * OpenID Connect sign-in for browser apps: routes: [sso] with sso = oidc({ issuer, clientId, clientSecret,
115
+ * session, userId }). The sign-in ends in the session's cookie.
116
+ */
117
+ export function oidc(options) {
118
+ if (!options || typeof options !== "object" || Array.isArray(options))
119
+ throw invalid("oidc() needs { issuer, clientId, session, userId }.");
120
+ for (const key of Object.keys(options))
121
+ if (!OPTIONS.includes(key))
122
+ throw invalid(`oidc() has no option "${key}".`, `Use ${OPTIONS.join(", ")}.`);
123
+ if (typeof options.issuer !== "string" || !options.issuer)
124
+ throw invalid("oidc() needs the issuer: the provider's URL as its OpenID configuration names it.");
125
+ httpsUrl(options.issuer, "issuer");
126
+ if (typeof options.clientId !== "string" || !options.clientId)
127
+ throw invalid("oidc() needs the clientId the provider gave this app.");
128
+ if (options.clientSecret !== undefined && (typeof options.clientSecret !== "string" || !options.clientSecret)) {
129
+ throw invalid("oidc() clientSecret must be a non-empty string, or left out for a public client.", "Keep the client secret in config({ secrets }) and pass it as clientSecret.");
130
+ }
131
+ const session = options.session;
132
+ if (!session || typeof session !== "object" || typeof session.signIn !== "function" || typeof session.resolve !== "function") {
133
+ throw invalid("oidc() needs session: the sessions() the sign-in ends in.", "Pass session = sessions({ secret, user }), and use it as the app's auth too: createApp({ auth: session }).");
134
+ }
135
+ if (typeof options.userId !== "function")
136
+ throw invalid("oidc() needs userId(claims, { db }): the id of the app's user for a verified sign-in, or null to refuse it.");
137
+ const redirectUri = options.redirectUri === undefined ? undefined : httpsUrl(options.redirectUri, "redirectUri").href;
138
+ const scopes = options.scopes ?? ["openid", "email", "profile"];
139
+ if (!Array.isArray(scopes) || scopes.some((s) => typeof s !== "string" || !/^[\x21\x23-\x5b\x5d-\x7e]+$/.test(s))) {
140
+ throw invalid('oidc() scopes must be a list of scope names, such as ["openid", "email", "profile"].');
141
+ }
142
+ const scope = [...new Set(["openid", ...scopes])].join(" ");
143
+ const basePath = options.basePath ?? "/auth";
144
+ if (typeof basePath !== "string" || !/^\/[A-Za-z0-9._~\-/]*$/.test(basePath) || basePath.includes("//") || !basePath.replace(/\/+$/, "")) {
145
+ throw invalid(`oidc() basePath must be a path such as "/auth", got ${JSON.stringify(basePath)}.`);
146
+ }
147
+ const base = basePath.replace(/\/+$/, "");
148
+ const extraParams = options.authorizationParams ?? {};
149
+ if (!extraParams || typeof extraParams !== "object" || Array.isArray(extraParams)) {
150
+ throw invalid('oidc() authorizationParams must be an object of parameter names and string values, such as { prompt: "select_account" }.');
151
+ }
152
+ for (const [key, value] of Object.entries(extraParams)) {
153
+ if (RESERVED_PARAMS.includes(key)) {
154
+ throw invalid(`oidc() authorizationParams cannot set "${key}": oidc() sets it itself.`, `Leave "${key}" out of authorizationParams; use scopes for the scope and redirectUri for the redirect URI.`);
155
+ }
156
+ if (!/^[A-Za-z0-9_.-]{1,64}$/.test(key) || typeof value !== "string" || value.length > 1000 || /[\u0000-\u001f\u007f]/.test(value)) {
157
+ throw invalid(`oidc() authorizationParams.${key} must be a string of at most 1000 characters without control characters, under a parameter name such as "prompt" or "domain_hint".`);
158
+ }
159
+ }
160
+ const postLogoutRedirectUri = options.postLogoutRedirectUri === undefined ? undefined : httpsUrl(options.postLogoutRedirectUri, "postLogoutRedirectUri").href;
161
+ const { issuer, clientId, clientSecret } = options;
162
+ // The provider's OpenID configuration: fetched on first use, kept for 10 minutes, and kept longer while the
163
+ // provider cannot be reached.
164
+ let discovered;
165
+ let loading;
166
+ const loadDiscovery = async () => {
167
+ const url = `${issuer.replace(/\/+$/, "")}/.well-known/openid-configuration`;
168
+ const response = await fetch(url, { headers: { accept: "application/json" }, redirect: "error", signal: AbortSignal.timeout(FETCH_TIMEOUT) });
169
+ if (!response.ok)
170
+ throw new Error(`${url} answered ${response.status}`);
171
+ const body = (await response.json());
172
+ if (!body || typeof body !== "object")
173
+ throw new Error(`${url} did not answer a JSON object`);
174
+ if (body.issuer !== issuer)
175
+ throw new Error(`the OpenID configuration names the issuer ${JSON.stringify(body.issuer)}, not ${JSON.stringify(issuer)}`);
176
+ for (const key of ["authorization_endpoint", "token_endpoint", "jwks_uri"]) {
177
+ if (typeof body[key] !== "string")
178
+ throw new Error(`the OpenID configuration has no ${key}`);
179
+ httpsUrl(body[key], key);
180
+ }
181
+ if (body.end_session_endpoint !== undefined) {
182
+ if (typeof body.end_session_endpoint !== "string")
183
+ throw new Error("the OpenID configuration's end_session_endpoint is not a URL");
184
+ httpsUrl(body.end_session_endpoint, "end_session_endpoint");
185
+ }
186
+ return body;
187
+ };
188
+ const discovery = async () => {
189
+ if (discovered && Date.now() - discovered.at < DISCOVERY_CACHE_MS)
190
+ return discovered.value;
191
+ try {
192
+ loading ??= loadDiscovery().finally(() => (loading = undefined));
193
+ const value = await loading;
194
+ discovered = { value, at: Date.now() };
195
+ return value;
196
+ }
197
+ catch (error) {
198
+ if (discovered)
199
+ return discovered.value;
200
+ throw unreachable("its OpenID configuration could not be loaded", error);
201
+ }
202
+ };
203
+ const verifyIdToken = createJwtVerifier({
204
+ issuer,
205
+ audiences: [clientId],
206
+ allowed: ASYMMETRIC,
207
+ tolerance: 60,
208
+ cacheMs: DISCOVERY_CACHE_MS,
209
+ discoverJwksUrl: async () => (await discovery()).jwks_uri,
210
+ });
211
+ const callbackUrl = (url) => redirectUri ?? new URL(`${base}/callback`, url).href;
212
+ const start = async (ctx) => {
213
+ const config = await discovery();
214
+ const pending = { s: random(), n: random(), v: random(), r: safeReturnTo(ctx.url.searchParams.get("returnTo")), e: Date.now() + PENDING_MS };
215
+ const challenge = base64url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(pending.v))));
216
+ const target = new URL(config.authorization_endpoint);
217
+ target.searchParams.set("response_type", "code");
218
+ target.searchParams.set("client_id", clientId);
219
+ target.searchParams.set("redirect_uri", callbackUrl(ctx.url));
220
+ target.searchParams.set("scope", scope);
221
+ target.searchParams.set("state", pending.s);
222
+ target.searchParams.set("nonce", pending.n);
223
+ target.searchParams.set("code_challenge", challenge);
224
+ target.searchParams.set("code_challenge_method", "S256");
225
+ for (const [key, value] of Object.entries(extraParams))
226
+ target.searchParams.set(key, value);
227
+ // Per sign-in: ?loginHint= fills in the user's name at the provider, ?prompt= asks it to show the login or
228
+ // account picker again. Anything else in the query is ignored.
229
+ const loginHint = ctx.url.searchParams.get("loginHint");
230
+ if (loginHint && loginHint.length <= 256 && !/[\u0000-\u001f\u007f]/.test(loginHint))
231
+ target.searchParams.set("login_hint", loginHint);
232
+ const prompt = ctx.url.searchParams.get("prompt");
233
+ if (prompt && prompt.split(" ").every((p) => PROMPTS.includes(p)))
234
+ target.searchParams.set("prompt", prompt);
235
+ const cookie = cookieFor(ctx.url);
236
+ ctx.header("set-cookie", `${cookie.name}=${base64url(new TextEncoder().encode(JSON.stringify(pending)))}; ${cookie.attributes}; Max-Age=${PENDING_MS / 1000}`);
237
+ ctx.header("cache-control", "no-store");
238
+ return new Response(null, { status: 302, headers: { location: target.href } });
239
+ };
240
+ const exchange = async (config, code, verifier, url) => {
241
+ const form = new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: callbackUrl(url), code_verifier: verifier });
242
+ const headers = { "content-type": "application/x-www-form-urlencoded", accept: "application/json" };
243
+ const methods = Array.isArray(config.token_endpoint_auth_methods_supported) ? config.token_endpoint_auth_methods_supported : undefined;
244
+ if (clientSecret === undefined)
245
+ form.set("client_id", clientId);
246
+ else if (methods && !methods.includes("client_secret_basic") && methods.includes("client_secret_post")) {
247
+ form.set("client_id", clientId);
248
+ form.set("client_secret", clientSecret);
249
+ }
250
+ else {
251
+ // client_secret_basic, the default of OpenID Connect: both parts form-encoded first (RFC 6749, section 2.3.1).
252
+ const encode = (value) => encodeURIComponent(value).replace(/%20/g, "+");
253
+ headers.authorization = `Basic ${btoa(`${encode(clientId)}:${encode(clientSecret)}`)}`;
254
+ }
255
+ let response;
256
+ let body;
257
+ try {
258
+ response = await fetch(config.token_endpoint, { method: "POST", headers, body: form.toString(), redirect: "error", signal: AbortSignal.timeout(FETCH_TIMEOUT) });
259
+ body = (await response.json()) ?? {};
260
+ }
261
+ catch (error) {
262
+ throw unreachable("its token endpoint did not answer", error);
263
+ }
264
+ if (!response.ok) {
265
+ const error = typeof body.error === "string" && /^[\w.-]{1,64}$/.test(body.error) ? body.error : `status ${response.status}`;
266
+ // A wrong client id or secret is the app's configuration; anything else (a used or expired code) is this sign-in.
267
+ if (error === "invalid_client" || error === "unauthorized_client" || response.status >= 500)
268
+ throw unreachable(`its token endpoint refused the code exchange (${error})`, error);
269
+ throw failed(`the identity provider refused the code (${error})`);
270
+ }
271
+ if (typeof body.id_token !== "string")
272
+ throw failed("the identity provider sent no ID token (is the openid scope allowed for this app?)");
273
+ const tokens = { idToken: body.id_token };
274
+ if (typeof body.access_token === "string")
275
+ tokens.accessToken = body.access_token;
276
+ if (typeof body.refresh_token === "string")
277
+ tokens.refreshToken = body.refresh_token;
278
+ if (typeof body.expires_in === "number")
279
+ tokens.expiresIn = body.expires_in;
280
+ if (typeof body.scope === "string")
281
+ tokens.scope = body.scope;
282
+ return tokens;
283
+ };
284
+ const finish = async (ctx) => {
285
+ const params = ctx.url.searchParams;
286
+ const cookie = cookieFor(ctx.url);
287
+ // The pending sign-in is used once, whatever happens.
288
+ ctx.header("set-cookie", `${cookie.name}=; ${cookie.attributes}; Max-Age=0`);
289
+ ctx.header("cache-control", "no-store");
290
+ let pending;
291
+ try {
292
+ const raw = readCookie(ctx.req.headers.get("cookie"), cookie.name);
293
+ if (raw && raw.length < 4096) {
294
+ const parsed = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(raw.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
295
+ if (["s", "n", "v", "r"].every((k) => typeof parsed[k] === "string") && typeof parsed.e === "number")
296
+ pending = parsed;
297
+ }
298
+ }
299
+ catch {
300
+ pending = undefined;
301
+ }
302
+ if (!pending || pending.e <= Date.now())
303
+ throw failed("it expired or was started in another browser");
304
+ const state = params.get("state");
305
+ if (!state || !safeEqual(state, pending.s))
306
+ throw failed("the state does not match the sign-in this browser started");
307
+ // RFC 9207: a provider that names itself in the redirect must be the configured one (mix-up attacks).
308
+ const iss = params.get("iss");
309
+ if (iss !== null && iss !== issuer)
310
+ throw failed("the answer came from another identity provider");
311
+ const error = params.get("error");
312
+ if (error !== null)
313
+ throw failed(`the identity provider answered ${/^[\w.-]{1,64}$/.test(error) ? error : "an error"}`);
314
+ const code = params.get("code");
315
+ if (!code)
316
+ throw failed("the identity provider sent no code");
317
+ const config = await discovery();
318
+ const tokens = await exchange(config, code, pending.v, ctx.url);
319
+ const claims = (await verifyIdToken(tokens.idToken));
320
+ if (!claims)
321
+ throw failed("the ID token is not valid for this app (signature, issuer, audience or expiry)");
322
+ if (typeof claims.nonce !== "string" || !safeEqual(claims.nonce, pending.n))
323
+ throw failed("the ID token's nonce does not match this sign-in");
324
+ if (Array.isArray(claims.aud) && claims.aud.length > 1 && claims.azp !== clientId)
325
+ throw failed("the ID token was issued to another app");
326
+ const userId = await options.userId(claims, { db: ctx.db, req: ctx.req, tokens });
327
+ if (userId === null || userId === undefined) {
328
+ throw new InferloError("IL3011", "This account may not sign in to the app.", { status: 403, fix: "Ask an administrator of the app for access." });
329
+ }
330
+ if (typeof userId !== "string" || !userId) {
331
+ throw new InferloError("IL6047", "oidc() userId must return the user's id as a non-empty string, or null to refuse the sign-in.", { fix: "Return the id of the app's user: userId: async (claims, { db }) => (await db.users.findOne({ email: claims.email }))?.id ?? null." });
332
+ }
333
+ await session.signIn(ctx, userId);
334
+ // The ID token is kept (HttpOnly) for signing out at the provider later, as id_token_hint.
335
+ const idCookie = cookieFor(ctx.url, "inferlo_oidc_id");
336
+ if (tokens.idToken.length <= MAX_ID_TOKEN_COOKIE && /^[\w-]+\.[\w-]+\.[\w-]*$/.test(tokens.idToken)) {
337
+ ctx.header("set-cookie", `${idCookie.name}=${tokens.idToken}; ${idCookie.attributes}; Max-Age=${ID_TOKEN_COOKIE_SECONDS}`);
338
+ }
339
+ else {
340
+ ctx.header("set-cookie", `${idCookie.name}=; ${idCookie.attributes}; Max-Age=0`);
341
+ }
342
+ return new Response(null, { status: 302, headers: { location: pending.r } });
343
+ };
344
+ const logout = async (ctx) => {
345
+ // A signed-in browser must send the session's CSRF token (IL3005), so other sites cannot sign users out.
346
+ await session.resolve(ctx.req, { db: ctx.db, url: ctx.url });
347
+ session.signOut(ctx);
348
+ const idCookie = cookieFor(ctx.url, "inferlo_oidc_id");
349
+ const idToken = readCookie(ctx.req.headers.get("cookie"), idCookie.name);
350
+ ctx.header("set-cookie", `${idCookie.name}=; ${idCookie.attributes}; Max-Age=0`);
351
+ ctx.header("cache-control", "no-store");
352
+ const afterLogout = postLogoutRedirectUri ?? new URL("/", ctx.url).href;
353
+ let endSession;
354
+ try {
355
+ endSession = (await discovery()).end_session_endpoint;
356
+ }
357
+ catch {
358
+ // The app's session is over; a provider that cannot be reached cannot end its own.
359
+ endSession = undefined;
360
+ }
361
+ if (!endSession)
362
+ return new Response(null, { status: 303, headers: { location: afterLogout } });
363
+ const target = new URL(endSession);
364
+ // Only an ID token this provider issued is sent back to it (with several providers, each has its basePath).
365
+ if (idToken && idToken.length <= MAX_ID_TOKEN_COOKIE && issuerOf(idToken) === issuer)
366
+ target.searchParams.set("id_token_hint", idToken);
367
+ target.searchParams.set("client_id", clientId);
368
+ target.searchParams.set("post_logout_redirect_uri", afterLogout);
369
+ return new Response(null, { status: 303, headers: { location: target.href } });
370
+ };
371
+ return {
372
+ oidcLogin: route.get(`${base}/login`, { auth: "public", summary: "Sign in with the identity provider (OpenID Connect)" }).handle(start),
373
+ oidcCallback: route.get(`${base}/callback`, { auth: "public", summary: "Finish the sign-in with the identity provider (OpenID Connect)" }).handle(finish),
374
+ oidcLogout: route.post(`${base}/logout`, { auth: "public", summary: "Sign out of the app and at the identity provider (OpenID Connect)" }).handle(logout),
375
+ loginPath: `${base}/login`,
376
+ logoutPath: `${base}/logout`,
377
+ };
378
+ }
379
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
package/dist/openapi.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /** OpenAPI 3.1 document generated from routes. Served at /openapi.json by default. */
2
2
  import { describeDeprecation, resolveDeprecation } from "./deprecation.js";
3
3
  import { toolName } from "./mcp.js";
4
+ import { containsFile } from "./schema.js";
4
5
  const errorSchema = {
5
6
  type: "object",
6
7
  properties: {
@@ -18,10 +19,24 @@ const errorSchema = {
18
19
  },
19
20
  required: ["error"],
20
21
  };
22
+ // A body with t.file() fields is multipart/form-data, with each file field's accepted types in its encoding.
23
+ function requestBody(body) {
24
+ const schema = body.toJSONSchema();
25
+ if (!containsFile(body))
26
+ return { required: !body.def.optional, content: { "application/json": { schema } } };
27
+ const encoding = {};
28
+ for (const [name, field] of Object.entries(body.def.shape ?? {})) {
29
+ const types = field.def.kind === "file" ? field.def.fileTypes : field.def.kind === "array" ? field.def.item.def.fileTypes : undefined;
30
+ if (types?.length)
31
+ encoding[name] = { contentType: types.join(", ") };
32
+ }
33
+ return { required: !body.def.optional, content: { "multipart/form-data": Object.keys(encoding).length ? { schema, encoding } : { schema } } };
34
+ }
21
35
  export function buildOpenAPI(app, options) {
22
36
  const paths = {};
23
37
  // A version's document has its routes and the routes outside every version.
24
- const routes = options.version === undefined ? app.routes : app.routes.filter((r) => r.version === undefined || r.version === options.version);
38
+ // WebSocket routes (route.ws) have no HTTP operation to describe; describe() lists them.
39
+ const routes = (options.version === undefined ? app.routes : app.routes.filter((r) => r.version === undefined || r.version === options.version)).filter((r) => !r.websocket);
25
40
  for (const route of routes) {
26
41
  const o = route.options;
27
42
  const openPath = route.path.replace(/:([^/]+)/g, "{$1}").replace(/\/\*$/, "/{wildcard}");
@@ -45,7 +60,7 @@ export function buildOpenAPI(app, options) {
45
60
  // Routes of a feature module are tagged with its name unless they set their own tags.
46
61
  tags: o.tags ?? (options.modules?.routeModule.has(route) ? [options.modules.routeModule.get(route)] : undefined),
47
62
  parameters: parameters.length ? parameters : undefined,
48
- requestBody: o.body ? { required: !o.body.def.optional, content: { "application/json": { schema: o.body.toJSONSchema() } } } : undefined,
63
+ requestBody: o.body ? requestBody(o.body) : undefined,
49
64
  responses: {
50
65
  "200": { description: "Success", content: o.response ? { "application/json": { schema: o.response.toJSONSchema() } } : undefined },
51
66
  ...(o.body || o.query || o.params ? { "422": { description: "Validation failed (IL1001)", content: { "application/json": { schema: errorSchema } } } } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Passport.js users as an Inferlo app's auth, for an Inferlo app mounted in Express (or node:http) with
3
+ * toNodeHandler, without a runtime dependency on Passport:
4
+ *
5
+ * import session from "express-session";
6
+ * import passport from "passport";
7
+ * import { createApp, toNodeHandler } from "@browserlessapi/inferlo";
8
+ * import { passportSession } from "@browserlessapi/inferlo/passport";
9
+ *
10
+ * const app = createApp({ routes, auth: passportSession() });
11
+ * server.use(session({ secret, resave: false, saveUninitialized: false }));
12
+ * server.use(passport.session()); // Passport puts the signed-in user on req.user
13
+ * server.use("/api", toNodeHandler(app)); // Inferlo routes get that user as ctx.user
14
+ *
15
+ * Inferlo reads req.user from the Node.js request the Web Request was built from; Passport's strategies,
16
+ * serializeUser and deserializeUser stay as they are. Requests that change data (POST, PUT, PATCH, DELETE) with a
17
+ * Passport session from express-session are refused with IL3007 when a browser sends them from another site.
18
+ */
19
+ import type { AuthConfig } from "./app.ts";
20
+ import type { AppDb, AppUser } from "./route.ts";
21
+ /** The parts of a Node.js request that Passport and express-session set and Inferlo reads. */
22
+ export interface PassportRequestLike {
23
+ /** The user Passport's deserializeUser (or a strategy with session: false) put on the request. */
24
+ user?: unknown;
25
+ /** express-session's session; Passport keeps its login in session.passport.user. */
26
+ session?: {
27
+ passport?: {
28
+ user?: unknown;
29
+ };
30
+ } | null;
31
+ }
32
+ export interface PassportSessionOptions<PassportUser = any> {
33
+ /**
34
+ * The app's user for Passport's req.user, or null to refuse it. Default: { id, email, name, roles } with id from
35
+ * user.id (a string, or a number turned into a string), name from user.name or user.displayName, and roles from
36
+ * user.roles.
37
+ */
38
+ user?: (user: PassportUser, ctx: {
39
+ db: AppDb;
40
+ req: Request;
41
+ nodeRequest: PassportRequestLike;
42
+ }) => AppUser | null | undefined | Promise<AppUser | null | undefined>;
43
+ /** Origins ("https://admin.example.com") whose pages may change data with the session cookie, besides the app's own. */
44
+ trustedOrigins?: string[];
45
+ /** Roles of a user, checked when a route uses auth: "<role>". Default: user.roles ?? []. */
46
+ roles?: (user: AppUser) => string[];
47
+ /** The tenant of a user, for tenant-scoped models. Default: user.tenantId. */
48
+ tenant?: (user: AppUser) => string | null | undefined;
49
+ }
50
+ /**
51
+ * Passport's req.user as the app's auth: createApp({ auth: passportSession() }), with the app mounted in Express
52
+ * after passport.session() through toNodeHandler. Routes get the user that options.user returns for req.user.
53
+ */
54
+ export declare function passportSession<PassportUser = any>(options?: PassportSessionOptions<PassportUser>): AuthConfig;
@@ -0,0 +1,68 @@
1
+ import { nodeRequests } from "./client-ip.js";
2
+ import { checkCrossSite, originOf } from "./cross-site.js";
3
+ import { InferloError } from "./errors.js";
4
+ const fix = "Use passportSession({ user, trustedOrigins, roles, tenant }) and mount the app in Express after passport.session(): server.use(\"/api\", toNodeHandler(app)).";
5
+ const idFix = "Give Passport's user an id (deserializeUser should return the user with its id), or map it yourself: passportSession({ user: (u) => ({ id: String(u.userId), roles: u.roles }) }).";
6
+ const mountFix = 'Mount the app in Express after Passport: server.use(session({ ... })); server.use(passport.session()); server.use("/api", toNodeHandler(app)). app.listen() and app.fetch() have no Express request, so Passport never runs for them.';
7
+ function defaultUser(user) {
8
+ const u = (user && typeof user === "object" ? user : {});
9
+ const id = typeof u.id === "number" && Number.isFinite(u.id) ? String(u.id) : u.id;
10
+ if (typeof id !== "string" || !id)
11
+ throw new InferloError("IL6046", "Passport's user (req.user) has no id.", { fix: idFix });
12
+ const roles = Array.isArray(u.roles) ? u.roles.filter((r) => typeof r === "string") : [];
13
+ const out = { id, roles };
14
+ if (typeof u.email === "string")
15
+ out.email = u.email;
16
+ const name = typeof u.name === "string" ? u.name : typeof u.displayName === "string" ? u.displayName : undefined;
17
+ if (name !== undefined)
18
+ out.name = name;
19
+ // Apps that register their own user type (Register) map Passport's user with options.user.
20
+ return out;
21
+ }
22
+ /**
23
+ * Passport's req.user as the app's auth: createApp({ auth: passportSession() }), with the app mounted in Express
24
+ * after passport.session() through toNodeHandler. Routes get the user that options.user returns for req.user.
25
+ */
26
+ export function passportSession(options = {}) {
27
+ if (!options || typeof options !== "object" || Array.isArray(options))
28
+ throw new InferloError("IL6045", "passportSession() options must be an object.", { fix });
29
+ for (const key of Object.keys(options)) {
30
+ if (!["user", "trustedOrigins", "roles", "tenant"].includes(key)) {
31
+ throw new InferloError("IL6045", `passportSession() has no option "${key}".`, { fix: "Use user, trustedOrigins, roles or tenant." });
32
+ }
33
+ }
34
+ for (const key of ["user", "roles", "tenant"]) {
35
+ if (options[key] !== undefined && typeof options[key] !== "function")
36
+ throw new InferloError("IL6045", `passportSession() ${key} must be a function.`, { fix });
37
+ }
38
+ if (options.trustedOrigins !== undefined && !Array.isArray(options.trustedOrigins)) {
39
+ throw new InferloError("IL6045", "passportSession() trustedOrigins must be a list of origins.", { fix: 'Use trustedOrigins: ["https://admin.example.com"].' });
40
+ }
41
+ const trusted = new Set((options.trustedOrigins ?? []).map((value) => {
42
+ const origin = originOf(value);
43
+ if (!origin || origin !== String(value).replace(/\/+$/, "")) {
44
+ throw new InferloError("IL6045", `passportSession() trustedOrigins: ${JSON.stringify(value)} is not an origin.`, { fix: 'Use origins without a path, such as "https://admin.example.com".' });
45
+ }
46
+ return origin;
47
+ }));
48
+ const toUser = options.user;
49
+ return {
50
+ async resolve(req, { db, url }) {
51
+ const nodeRequest = nodeRequests.get(req);
52
+ if (!nodeRequest)
53
+ throw new InferloError("IL6046", "passportSession() needs the app mounted in Express (or node:http) with toNodeHandler.", { fix: mountFix });
54
+ const passportUser = nodeRequest.user;
55
+ if (passportUser === undefined || passportUser === null || passportUser === false)
56
+ return null;
57
+ // A login kept in express-session rides on a cookie the browser sends by itself: refuse it from other sites.
58
+ const fromSession = nodeRequest.session?.passport?.user !== undefined;
59
+ checkCrossSite(req, url, trusted, () => fromSession, "passportSession({ trustedOrigins: [...] })");
60
+ if (toUser)
61
+ return (await toUser(passportUser, { db, req, nodeRequest })) ?? null;
62
+ return defaultUser(passportUser);
63
+ },
64
+ ...(options.roles ? { roles: options.roles } : {}),
65
+ ...(options.tenant ? { tenant: options.tenant } : {}),
66
+ };
67
+ }
68
+ //# sourceMappingURL=passport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passport.js","sourceRoot":"","sources":["../src/passport.ts"],"names":[],"mappings":"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"}