@protobase/server 0.9.0 → 0.10.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 (41) hide show
  1. package/dist/active-role.js +8 -0
  2. package/dist/admin-options.d.ts +8 -0
  3. package/dist/api-cors.d.ts +5 -0
  4. package/dist/api-cors.js +53 -0
  5. package/dist/better-auth/admin-only.js +5 -3
  6. package/dist/better-auth/authenticator.d.ts +1 -1
  7. package/dist/better-auth/authenticator.js +1 -1
  8. package/dist/better-auth/create-auth.d.ts +166 -160
  9. package/dist/better-auth/create-auth.js +40 -10
  10. package/dist/better-auth/operator-provider.d.ts +2 -1
  11. package/dist/better-auth/origins.d.ts +4 -0
  12. package/dist/better-auth/origins.js +43 -0
  13. package/dist/better-auth/passkey-domain.js +41 -0
  14. package/dist/better-auth/passkey-plugin.js +151 -0
  15. package/dist/better-auth/roles/active-role.js +26 -0
  16. package/dist/better-auth/roles/roles-plugin.js +73 -0
  17. package/dist/better-auth/roles/session-role.js +28 -0
  18. package/dist/better-auth/staff-sign-in-endpoints.js +9 -2
  19. package/dist/better-auth/status-route.js +5 -1
  20. package/dist/better-auth/tokens.d.ts +2 -1
  21. package/dist/better-auth/tokens.js +5 -2
  22. package/dist/check-shell.js +11 -2
  23. package/dist/create-admin.d.ts +5 -3
  24. package/dist/create-admin.js +23 -2
  25. package/dist/index.d.ts +4 -2
  26. package/dist/index.js +2 -1
  27. package/dist/jobs/failed-jobs.js +10 -10
  28. package/dist/jobs/functions-sql.js +8 -5
  29. package/dist/jobs/runtime.js +2 -0
  30. package/dist/meta.js +9 -2
  31. package/dist/openapi/components.js +1 -1
  32. package/dist/openapi/document.js +7 -5
  33. package/dist/openapi/naming.js +2 -1
  34. package/dist/resource-source.d.ts +3 -2
  35. package/dist/resource-source.js +3 -1
  36. package/dist/routes/docs.js +2 -2
  37. package/dist/webhooks/table/owner-roles.js +15 -9
  38. package/dist/webhooks/table/plugin.d.ts +1 -1
  39. package/dist/webhooks/table/plugin.js +1 -1
  40. package/dist/webhooks/table/validate-hook.js +1 -1
  41. package/package.json +4 -4
@@ -0,0 +1,8 @@
1
+ //#region src/active-role.ts
2
+ /**
3
+ * The one role a session works as, which picks its bottom bar: the first of its roles. A Better Auth token carries the
4
+ * session's active role alone (see `narrowClaims`); with another authenticator, the caller's first role.
5
+ */
6
+ const activeRole = (session) => session.user.roles[0];
7
+ //#endregion
8
+ export { activeRole };
@@ -1,4 +1,5 @@
1
1
  import { AuditQueue, PipelineHook, ScanGuardMode } from "./types.js";
2
+ import { CorsOrigins } from "./api-cors.js";
2
3
  import { AssistantOptions } from "./assistant/assistant-settings.js";
3
4
  import { RuntimeOptions } from "./runtime/runtime-settings.js";
4
5
  import { ResolveOptions } from "@protobase/schema";
@@ -29,6 +30,13 @@ export type AdminOptions = {
29
30
  onUnhandledError?: (error: unknown) => void;
30
31
  /** The assistant in the admin app's dock: a backend elsewhere or the built-in one (see `AssistantOptions`); `false` turns it off. */
31
32
  assistant?: AssistantOptions | false;
33
+ /**
34
+ * The websites whose browser code may call the API (`/api/*` but `/api/auth/*`) besides the app's own origins (see
35
+ * `createAuth`'s `origins`): a list of origins, or `*` for any. Defaults to the one the platform passes in
36
+ * `PROTOBASE_CORS_ORIGINS` (`*` or comma separated), when set. With neither this nor other origins, the API sends no
37
+ * CORS headers.
38
+ */
39
+ cors?: CorsOrigins;
32
40
  /** The runtime updates endpoint behind the top bar's update button (see `RuntimeOptions`); `false` turns it off. */
33
41
  runtime?: RuntimeOptions | false;
34
42
  };
@@ -0,0 +1,5 @@
1
+ import "hono";
2
+ //#region src/api-cors.d.ts
3
+ /** The websites whose browser code may call the API: `*` for any, or a list of origins such as `https://shop.com`. */
4
+ export type CorsOrigins = '*' | string[];
5
+ //#endregion
@@ -0,0 +1,53 @@
1
+ import { readList } from "./better-auth/origins.js";
2
+ import { cors } from "hono/cors";
3
+ //#region src/api-cors.ts
4
+ const corsOriginsVariable = "PROTOBASE_CORS_ORIGINS";
5
+ /** The origins in `PROTOBASE_CORS_ORIGINS`, `*` or comma separated; `undefined` when it is unset. One that is not a URL stops the server at startup. */
6
+ const readCorsOrigins = (env) => {
7
+ const value = env[corsOriginsVariable]?.trim();
8
+ if (!value) return void 0;
9
+ if (value === "*") return "*";
10
+ return readList(value).map((origin) => {
11
+ if (!URL.canParse(origin)) throw new Error(`${corsOriginsVariable} lists "${origin}", which is not a URL such as https://shop.com`);
12
+ return new URL(origin).origin;
13
+ });
14
+ };
15
+ const allowHeaders = [
16
+ "authorization",
17
+ "content-type",
18
+ "if-match",
19
+ "if-none-match"
20
+ ];
21
+ const exposeHeaders = [
22
+ "location",
23
+ "etag",
24
+ "x-meta-version",
25
+ "x-protobase-warning"
26
+ ];
27
+ /**
28
+ * CORS for the API: an allowed origin gets `Access-Control-Allow-Origin` (`*` when any is), preflights are answered
29
+ * before authentication and kept for ten minutes.
30
+ */
31
+ const apiCors = ({ own, allowed, skip }) => {
32
+ const origin = (requestOrigin) => {
33
+ if (allowed === "*") return "*";
34
+ return own.includes(requestOrigin) || allowed.includes(requestOrigin) ? requestOrigin : null;
35
+ };
36
+ const handler = cors({
37
+ origin,
38
+ allowHeaders,
39
+ exposeHeaders,
40
+ maxAge: 600,
41
+ allowMethods: [
42
+ "GET",
43
+ "HEAD",
44
+ "POST",
45
+ "PUT",
46
+ "PATCH",
47
+ "DELETE"
48
+ ]
49
+ });
50
+ return (c, next) => skip(c.req.path) ? next() : handler(c, next);
51
+ };
52
+ //#endregion
53
+ export { apiCors, corsOriginsVariable, readCorsOrigins };
@@ -1,9 +1,11 @@
1
- import { parseRoles } from "./parse-roles.js";
1
+ import "./organizations/role-definitions.js";
2
+ import { sessionRoles } from "./roles/session-role.js";
2
3
  import { APIError, createAuthMiddleware, sessionMiddleware } from "better-auth/api";
3
4
  //#region src/better-auth/admin-only.ts
4
- /** Lets a request through only with the session of an admin; others get `403 ADMIN_ONLY` with `message`. */
5
+ /** Lets a request through only with the session of someone acting as an admin; others get `403 ADMIN_ONLY` with `message`. */
5
6
  const adminOnly = (message) => createAuthMiddleware({ use: [sessionMiddleware] }, async (ctx) => {
6
- if (!parseRoles(ctx.context.session.user.role).includes("admin")) throw new APIError("FORBIDDEN", {
7
+ const { active } = await sessionRoles(ctx.context, ctx.context.session);
8
+ if (active !== "admin") throw new APIError("FORBIDDEN", {
7
9
  code: "ADMIN_ONLY",
8
10
  message
9
11
  });
@@ -7,7 +7,7 @@ export type BetterAuthAuthenticatorOptions = {
7
7
  auth?: AdminAuth;
8
8
  /** Or its JWKS endpoint, for an API that runs apart from the auth server. Needs `issuer`. */
9
9
  jwksUrl?: string | URL;
10
- /** Defaults to the auth instance's `baseURL`; it is both issuer and audience of its tokens. */
10
+ /** Defaults to the auth instance's `issuer`, its `baseURL`; it is both issuer and audience of its tokens. */
11
11
  issuer?: string;
12
12
  /** The one tenant everyone works in, for an app without organizations; never read from the token. */
13
13
  tenant?: TenantValue | (() => Promise<TenantValue | undefined>);
@@ -9,7 +9,7 @@ import { cachedKeys } from "./jwks-cache.js";
9
9
  * role, is left out.
10
10
  */
11
11
  const betterAuthAuthenticator = (options) => {
12
- const issuer = options.issuer ?? options.auth?.options.baseURL;
12
+ const issuer = options.issuer ?? options.auth?.issuer;
13
13
  if (!issuer) throw new Error("betterAuthAuthenticator needs `issuer` (or an `auth` instance with a baseURL)");
14
14
  if (!options.auth && !options.jwksUrl) throw new Error("betterAuthAuthenticator needs `auth` or `jwksUrl`");
15
15
  const organizations = options.organizations ?? Boolean(options.auth?.organizations);
@@ -1,8 +1,8 @@
1
1
  import { SignInPolicy } from "./sign-in-policy.js";
2
2
  import { StaffSignIn } from "./staff-log.js";
3
- import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js";
4
3
  import { Mailer } from "../mail/smtp-mailer.js";
5
4
  import { OperatorProvider } from "./operator-provider.js";
5
+ import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js";
6
6
  import { OrganizationRole } from "./organizations/store.js";
7
7
  import { OrganizationsOptions, ResolvedOrganizations } from "./organizations/options.js";
8
8
  import { PlatformSignIn } from "./platform-sign-in.js";
@@ -12,8 +12,21 @@ import { BetterAuthOptions } from "better-auth";
12
12
  export type CreateAuthOptions = {
13
13
  /** A `pg` Pool, or `{ dialect, type: 'postgres', schemaName? }` for a Kysely dialect: the admin store, apart from the application's tables (its own schema or database). */
14
14
  database: BetterAuthOptions['database'];
15
- /** Public origin of the API, for example `https://admin.example.com`. Issuer and audience of the tokens; passkeys belong to its host. */
15
+ /** Public origin of the API, for example `https://admin.example.com`. Issuer and audience of the tokens; new passkeys are made for its host unless `passkeyDomain` names another. */
16
16
  baseURL: string;
17
+ /**
18
+ * The other origins the app is served on, for example `['https://admin.shop.com']`. A request to one of them is
19
+ * answered for that host: its sign-in callbacks and the links it mails, such as a password reset, stay there, and
20
+ * passkeys work on every origin. Tokens keep `baseURL` as issuer, whatever host issued them. Defaults to the
21
+ * comma-separated list the platform passes in `PROTOBASE_ORIGINS`, when set; none otherwise.
22
+ */
23
+ origins?: string[];
24
+ /**
25
+ * The host new passkeys are made for (their RP ID): the host of `baseURL` or of one of `origins`. Each passkey keeps
26
+ * the one it was made for, and works on every origin, so changing it breaks none. Defaults to the one the platform
27
+ * passes in `PROTOBASE_PASSKEY_DOMAIN`, when set, and to the host of `baseURL` otherwise.
28
+ */
29
+ passkeyDomain?: string;
17
30
  /** The name authenticator apps and passkey prompts show for this app. Default `Protobase`. */
18
31
  appName?: string;
19
32
  /** Signs cookies and encrypts the JWT private keys. Generate with `openssl rand -base64 32`. */
@@ -1075,26 +1088,26 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1075
1088
  user: import("better-auth").User & Record<string, any>;
1076
1089
  session: import("better-auth").Session & Record<string, any>;
1077
1090
  }) => Promise<{
1078
- email: string;
1079
1091
  role: any;
1092
+ email: string;
1080
1093
  } | {
1081
1094
  org?: undefined;
1082
1095
  org_role?: undefined;
1083
1096
  app_roles?: undefined;
1084
- email: string;
1085
1097
  role: any;
1098
+ email: string;
1086
1099
  } | {
1087
1100
  org: string;
1088
1101
  org_role: OrganizationRole;
1089
1102
  app_roles: string[];
1090
- email: string;
1091
1103
  role: any;
1104
+ email: string;
1092
1105
  } | {
1093
1106
  org: string;
1094
1107
  org_role?: undefined;
1095
1108
  app_roles?: undefined;
1096
- email: string;
1097
1109
  role: any;
1110
+ email: string;
1098
1111
  }>;
1099
1112
  };
1100
1113
  disableSettingJwtHeader: true;
@@ -1990,9 +2003,39 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1990
2003
  INVALID_TWO_FACTOR_COOKIE: import("better-auth").RawError<"INVALID_TWO_FACTOR_COOKIE">;
1991
2004
  };
1992
2005
  }, {
1993
- id: "passkey";
1994
- version: string;
1995
2006
  endpoints: {
2007
+ generatePasskeyAuthenticationOptions: import("better-auth").StrictEndpoint<"/passkey/generate-authenticate-options", {
2008
+ method: "GET";
2009
+ query: import("zod").ZodOptional<import("zod").ZodObject<{
2010
+ rpId: import("zod").ZodOptional<import("zod").ZodString>;
2011
+ }, import("zod/v4/core").$strip>>;
2012
+ }, import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
2013
+ verifyPasskeyAuthentication: import("better-auth").StrictEndpoint<"/passkey/verify-authentication", {
2014
+ method: "POST";
2015
+ body: import("zod").ZodObject<{
2016
+ response: import("zod").ZodRecord<import("zod").ZodAny, import("zod").ZodAny>;
2017
+ }, import("zod/v4/core").$strip>;
2018
+ }, {
2019
+ session: {
2020
+ id: string;
2021
+ createdAt: Date;
2022
+ updatedAt: Date;
2023
+ userId: string;
2024
+ expiresAt: Date;
2025
+ token: string;
2026
+ ipAddress?: string | null | undefined;
2027
+ userAgent?: string | null | undefined;
2028
+ };
2029
+ user: {
2030
+ id: string;
2031
+ createdAt: Date;
2032
+ updatedAt: Date;
2033
+ email: string;
2034
+ emailVerified: boolean;
2035
+ name: string;
2036
+ image?: string | null | undefined;
2037
+ };
2038
+ }>;
1996
2039
  generatePasskeyRegistrationOptions: import("better-auth").StrictEndpoint<"/passkey/generate-register-options", {
1997
2040
  method: "GET";
1998
2041
  use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
@@ -2133,100 +2176,6 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2133
2176
  };
2134
2177
  };
2135
2178
  }, import("@simplewebauthn/server").PublicKeyCredentialCreationOptionsJSON>;
2136
- generatePasskeyAuthenticationOptions: import("better-auth").StrictEndpoint<"/passkey/generate-authenticate-options", {
2137
- method: "GET";
2138
- metadata: {
2139
- openapi: {
2140
- operationId: string;
2141
- description: string;
2142
- responses: {
2143
- 200: {
2144
- description: string;
2145
- content: {
2146
- "application/json": {
2147
- schema: {
2148
- type: "object";
2149
- properties: {
2150
- challenge: {
2151
- type: string;
2152
- };
2153
- rp: {
2154
- type: string;
2155
- properties: {
2156
- name: {
2157
- type: string;
2158
- };
2159
- id: {
2160
- type: string;
2161
- };
2162
- };
2163
- };
2164
- user: {
2165
- type: string;
2166
- properties: {
2167
- id: {
2168
- type: string;
2169
- };
2170
- name: {
2171
- type: string;
2172
- };
2173
- displayName: {
2174
- type: string;
2175
- };
2176
- };
2177
- };
2178
- timeout: {
2179
- type: string;
2180
- };
2181
- allowCredentials: {
2182
- type: string;
2183
- items: {
2184
- type: string;
2185
- properties: {
2186
- id: {
2187
- type: string;
2188
- };
2189
- type: {
2190
- type: string;
2191
- };
2192
- transports: {
2193
- type: string;
2194
- items: {
2195
- type: string;
2196
- };
2197
- };
2198
- };
2199
- };
2200
- };
2201
- userVerification: {
2202
- type: string;
2203
- };
2204
- authenticatorSelection: {
2205
- type: string;
2206
- properties: {
2207
- authenticatorAttachment: {
2208
- type: string;
2209
- };
2210
- requireResidentKey: {
2211
- type: string;
2212
- };
2213
- userVerification: {
2214
- type: string;
2215
- };
2216
- };
2217
- };
2218
- extensions: {
2219
- type: string;
2220
- };
2221
- };
2222
- };
2223
- };
2224
- };
2225
- };
2226
- };
2227
- };
2228
- };
2229
- }, import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
2230
2179
  verifyPasskeyRegistration: import("better-auth").StrictEndpoint<"/passkey/verify-registration", {
2231
2180
  method: "POST";
2232
2181
  body: import("zod").ZodObject<{
@@ -2292,63 +2241,6 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2292
2241
  };
2293
2242
  };
2294
2243
  }, import("@better-auth/passkey").Passkey>;
2295
- verifyPasskeyAuthentication: import("better-auth").StrictEndpoint<"/passkey/verify-authentication", {
2296
- method: "POST";
2297
- body: import("zod").ZodObject<{
2298
- response: import("zod").ZodRecord<import("zod").ZodAny, import("zod").ZodAny>;
2299
- }, import("zod/v4/core").$strip>;
2300
- metadata: {
2301
- openapi: {
2302
- operationId: string;
2303
- description: string;
2304
- responses: {
2305
- 200: {
2306
- description: string;
2307
- content: {
2308
- "application/json": {
2309
- schema: {
2310
- type: "object";
2311
- properties: {
2312
- session: {
2313
- $ref: string;
2314
- };
2315
- user: {
2316
- $ref: string;
2317
- };
2318
- };
2319
- };
2320
- };
2321
- };
2322
- };
2323
- };
2324
- };
2325
- $Infer: {
2326
- body: {
2327
- response: import("@simplewebauthn/server").AuthenticationResponseJSON;
2328
- };
2329
- };
2330
- };
2331
- }, {
2332
- session: {
2333
- id: string;
2334
- createdAt: Date;
2335
- updatedAt: Date;
2336
- userId: string;
2337
- expiresAt: Date;
2338
- token: string;
2339
- ipAddress?: string | null | undefined;
2340
- userAgent?: string | null | undefined;
2341
- };
2342
- user: {
2343
- id: string;
2344
- createdAt: Date;
2345
- updatedAt: Date;
2346
- email: string;
2347
- emailVerified: boolean;
2348
- name: string;
2349
- image?: string | null | undefined;
2350
- };
2351
- }>;
2352
2244
  listPasskeys: import("better-auth").StrictEndpoint<"/passkey/list-user-passkeys", {
2353
2245
  method: "GET";
2354
2246
  use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
@@ -2515,6 +2407,13 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2515
2407
  schema: {
2516
2408
  passkey: {
2517
2409
  fields: {
2410
+ rpID: {
2411
+ type: "string";
2412
+ required: false;
2413
+ fieldName: string;
2414
+ input: false;
2415
+ defaultValue: () => string;
2416
+ };
2518
2417
  name: {
2519
2418
  type: "string";
2520
2419
  required: false;
@@ -2564,6 +2463,8 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2564
2463
  };
2565
2464
  };
2566
2465
  };
2466
+ id: "passkey";
2467
+ version: string;
2567
2468
  $ERROR_CODES: {
2568
2469
  CHALLENGE_NOT_FOUND: import("better-auth").RawError<"CHALLENGE_NOT_FOUND">;
2569
2470
  YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY: import("better-auth").RawError<"YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY">;
@@ -3663,6 +3564,101 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
3663
3564
  handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
3664
3565
  }[];
3665
3566
  };
3567
+ } | {
3568
+ id: "protobase-roles";
3569
+ schema: {
3570
+ session: {
3571
+ fields: {
3572
+ activeRole: {
3573
+ type: "string";
3574
+ required: false;
3575
+ input: false;
3576
+ fieldName: string;
3577
+ };
3578
+ };
3579
+ };
3580
+ user: {
3581
+ fields: {
3582
+ lastRole: {
3583
+ type: "string";
3584
+ required: false;
3585
+ input: false;
3586
+ fieldName: string;
3587
+ };
3588
+ };
3589
+ };
3590
+ };
3591
+ endpoints: {
3592
+ currentRole: import("better-auth").StrictEndpoint<"/role/current", {
3593
+ method: "GET";
3594
+ use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
3595
+ session: {
3596
+ session: Record<string, any> & {
3597
+ id: string;
3598
+ createdAt: Date;
3599
+ updatedAt: Date;
3600
+ userId: string;
3601
+ expiresAt: Date;
3602
+ token: string;
3603
+ ipAddress?: string | null | undefined;
3604
+ userAgent?: string | null | undefined;
3605
+ };
3606
+ user: Record<string, any> & {
3607
+ id: string;
3608
+ createdAt: Date;
3609
+ updatedAt: Date;
3610
+ email: string;
3611
+ emailVerified: boolean;
3612
+ name: string;
3613
+ image?: string | null | undefined;
3614
+ };
3615
+ };
3616
+ }>>[];
3617
+ }, {
3618
+ active: string | null;
3619
+ roles: {
3620
+ name: string;
3621
+ label: string;
3622
+ }[];
3623
+ }>;
3624
+ switchRole: import("better-auth").StrictEndpoint<"/role/switch", {
3625
+ method: "POST";
3626
+ use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
3627
+ session: {
3628
+ session: Record<string, any> & {
3629
+ id: string;
3630
+ createdAt: Date;
3631
+ updatedAt: Date;
3632
+ userId: string;
3633
+ expiresAt: Date;
3634
+ token: string;
3635
+ ipAddress?: string | null | undefined;
3636
+ userAgent?: string | null | undefined;
3637
+ };
3638
+ user: Record<string, any> & {
3639
+ id: string;
3640
+ createdAt: Date;
3641
+ updatedAt: Date;
3642
+ email: string;
3643
+ emailVerified: boolean;
3644
+ name: string;
3645
+ image?: string | null | undefined;
3646
+ };
3647
+ };
3648
+ }>>[];
3649
+ body: import("zod").ZodObject<{
3650
+ role: import("zod").ZodString;
3651
+ }, import("zod/v4/core").$strip>;
3652
+ }, {
3653
+ role: string;
3654
+ }>;
3655
+ };
3656
+ hooks: {
3657
+ before: {
3658
+ matcher: (ctx: import("better-auth").HookEndpointContext) => boolean;
3659
+ handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
3660
+ }[];
3661
+ };
3666
3662
  } | import("better-auth/plugins").DefaultOrganizationPlugin<{
3667
3663
  organizationHooks: {
3668
3664
  beforeCreateOrganization?: (data: {
@@ -4264,7 +4260,11 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
4264
4260
  socialProviders?: import("better-auth").SocialProviders | undefined;
4265
4261
  appName: string;
4266
4262
  database: any;
4267
- baseURL: string;
4263
+ baseURL: string | {
4264
+ allowedHosts: string[];
4265
+ fallback: string;
4266
+ protocol: "https" | "http";
4267
+ };
4268
4268
  basePath: string;
4269
4269
  secret: string;
4270
4270
  trustedOrigins: string[];
@@ -4281,6 +4281,12 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
4281
4281
  minPasswordLength: number;
4282
4282
  };
4283
4283
  }> & {
4284
+ /** Issuer and audience of the tokens: `baseURL`, on every origin. */
4285
+ issuer: string;
4286
+ /** Every origin the app is served on, `baseURL`'s first. */
4287
+ origins: string[];
4288
+ /** The RP ID of new passkeys. */
4289
+ passkeyDomain: string;
4284
4290
  roles: string[];
4285
4291
  roleDefinitions: RoleDefinitions;
4286
4292
  organizations: ResolvedOrganizations | undefined;
@@ -1,3 +1,4 @@
1
+ import { multiHostBaseURL, readOrigins, resolveOrigins } from "./origins.js";
1
2
  import { readMailSettings } from "../mail/mail-settings.js";
2
3
  import { smtpMailer } from "../mail/smtp-mailer.js";
3
4
  import { readOperatorSettings, resolveOperator } from "./operator-provider.js";
@@ -6,14 +7,17 @@ import { lastOrganizationField, organizationClaims, sessionOrganizationHooks } f
6
7
  import { organizationPlugin } from "./organizations/organization-plugin.js";
7
8
  import { protobaseOrganizationPlugin } from "./organizations/protobase-plugin.js";
8
9
  import { resolveOrganizations } from "./organizations/options.js";
10
+ import { passkeyHosts, readPasskeyDomain, resolvePasskeyDomain } from "./passkey-domain.js";
11
+ import { coreNames, pluginNames } from "./snake-case-names.js";
12
+ import { passkeyPlugin } from "./passkey-plugin.js";
9
13
  import { passwordResetOptions } from "./password-reset.js";
14
+ import { narrowClaims } from "./roles/active-role.js";
15
+ import { rolesPlugin } from "./roles/roles-plugin.js";
10
16
  import { platformSignInFor, platformSignInProvider, readPlatformSignIn, resolvePlatformSignIn } from "./platform-sign-in.js";
11
17
  import { signInPolicyPlugin } from "./policy-plugin.js";
12
18
  import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
13
- import { coreNames, pluginNames } from "./snake-case-names.js";
14
19
  import { staffSignInPlugin } from "./staff-plugin.js";
15
20
  import { twoFactorAfterPasswordOrCode } from "./two-factor-challenge.js";
16
- import { passkey } from "@better-auth/passkey";
17
21
  import { APIError } from "better-auth/api";
18
22
  import { betterAuth } from "better-auth";
19
23
  import { admin, emailOTP, genericOAuth, jwt } from "better-auth/plugins";
@@ -21,6 +25,8 @@ import { adminAc, userAc } from "better-auth/plugins/admin/access";
21
25
  //#region src/better-auth/create-auth.ts
22
26
  const platformOperator = () => readOperatorSettings(globalThis.process?.env ?? {});
23
27
  const platformSignInSettings = () => readPlatformSignIn(globalThis.process?.env ?? {});
28
+ const platformOrigins = () => readOrigins(globalThis.process?.env ?? {});
29
+ const platformPasskeyDomain = () => readPasskeyDomain(globalThis.process?.env ?? {});
24
30
  const platformMailer = () => {
25
31
  const settings = readMailSettings(globalThis.process?.env ?? {});
26
32
  return settings && smtpMailer(settings);
@@ -42,17 +48,19 @@ const organizationPaths = ["/organization/set-active", "/organization/accept-inv
42
48
  * The options `createAuth` gives Better Auth, with the roles, default role, mailer, operator provider and platform
43
49
  * sign-in provider already resolved.
44
50
  */
45
- const betterAuthOptions = (options, { roles, defaultRole, mailer, operator, platformSignIn, organizations, context = noContext }) => {
51
+ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, mailer, operator, platformSignIn, organizations, context = noContext }) => {
46
52
  const secure = options.baseURL.startsWith("https://");
47
53
  const signIn = platformSignInFor(platformSignIn, Object.keys(options.socialProviders ?? {}));
54
+ const hosts = passkeyHosts([options.baseURL, ...origins]);
48
55
  return {
49
56
  appName: options.appName ?? "Protobase",
50
57
  database: options.database,
51
- baseURL: options.baseURL,
58
+ baseURL: multiHostBaseURL(options.baseURL, origins),
52
59
  basePath: authBasePath,
53
60
  secret: options.secret,
54
61
  trustedOrigins: [
55
62
  options.baseURL,
63
+ ...origins,
56
64
  "https://*.trycloudflare.com",
57
65
  ...options.trustedOrigins ?? []
58
66
  ],
@@ -86,11 +94,17 @@ const betterAuthOptions = (options, { roles, defaultRole, mailer, operator, plat
86
94
  expirationTime: tokenLifetime,
87
95
  issuer: options.baseURL,
88
96
  audience: options.baseURL,
89
- definePayload: async ({ user, session }) => ({
90
- email: user.email,
91
- role: user.role,
92
- ...organizations && await organizationClaims((await context()).adapter, user, session.activeOrganizationId)
93
- })
97
+ definePayload: async ({ user, session }) => {
98
+ const { activeRole, activeOrganizationId } = session;
99
+ const claims = {
100
+ role: user.role,
101
+ ...organizations && await organizationClaims((await context()).adapter, user, activeOrganizationId)
102
+ };
103
+ return {
104
+ email: user.email,
105
+ ...narrowClaims(claims, [activeRole, user.lastRole], Boolean(organizations))
106
+ };
107
+ }
94
108
  },
95
109
  disableSettingJwtHeader: true
96
110
  }),
@@ -99,7 +113,11 @@ const betterAuthOptions = (options, { roles, defaultRole, mailer, operator, plat
99
113
  allowPasswordless: true,
100
114
  ...mailer && { otpOptions: twoFactorCodeOptions(mailer) }
101
115
  }),
102
- passkey({ schema: pluginNames.passkey }),
116
+ passkeyPlugin({
117
+ domain: passkeyDomain ?? hosts[0],
118
+ hosts,
119
+ formerDomain: hosts[0]
120
+ }),
103
121
  ...mailer ? [emailOTP(emailCodeOptions(mailer))] : [],
104
122
  ...signIn ? [genericOAuth({ config: [platformSignInProvider(signIn)] })] : [],
105
123
  signInPolicyPlugin({
@@ -115,6 +133,7 @@ const betterAuthOptions = (options, { roles, defaultRole, mailer, operator, plat
115
133
  ...operator && { operator },
116
134
  ...mailer && { mailer }
117
135
  }),
136
+ rolesPlugin({ definitions }),
118
137
  ...organizations ? [organizationPlugin({
119
138
  resolved: organizations,
120
139
  secret: options.secret,
@@ -186,11 +205,16 @@ const createAuth = (options) => {
186
205
  const signInSettings = options.signInProvider === void 0 ? platformSignInSettings() : options.signInProvider || void 0;
187
206
  const socialProviders = Object.keys(options.socialProviders ?? {});
188
207
  const platformSignIn = platformSignInFor(signInSettings && resolvePlatformSignIn(signInSettings), socialProviders);
208
+ const origins = resolveOrigins(options.baseURL, options.origins ?? platformOrigins());
209
+ const passkeyDomain = resolvePasskeyDomain(options.passkeyDomain ?? platformPasskeyDomain(), [options.baseURL, ...origins]);
189
210
  const organizations = options.organizations && resolveOrganizations(options.organizations, definitions, options.baseURL);
190
211
  let instance;
191
212
  const context = () => instance?.$context ?? Promise.reject(/* @__PURE__ */ new Error("The auth instance is not made yet"));
192
213
  const auth = betterAuth(betterAuthOptions(options, {
214
+ origins,
215
+ passkeyDomain,
193
216
  roles,
217
+ definitions,
194
218
  defaultRole,
195
219
  mailer,
196
220
  operator,
@@ -202,6 +226,12 @@ const createAuth = (options) => {
202
226
  }));
203
227
  instance = auth;
204
228
  return Object.assign(auth, {
229
+ /** Issuer and audience of the tokens: `baseURL`, on every origin. */
230
+ issuer: options.baseURL,
231
+ /** Every origin the app is served on, `baseURL`'s first. */
232
+ origins: [new URL(options.baseURL).origin, ...origins],
233
+ /** The RP ID of new passkeys. */
234
+ passkeyDomain,
205
235
  roles,
206
236
  roleDefinitions: definitions,
207
237
  organizations,