@protobase/server 0.48.0 → 0.49.1

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 (74) hide show
  1. package/dist/admin-options.d.ts +2 -2
  2. package/dist/app-identity.d.ts +2 -2
  3. package/dist/app-name.d.ts +2 -0
  4. package/dist/better-auth/auth-settings.d.ts +8773 -0
  5. package/dist/better-auth/auth-settings.js +43 -0
  6. package/dist/better-auth/authenticate-settings.d.ts +15 -0
  7. package/dist/better-auth/authenticate-settings.js +12 -0
  8. package/dist/better-auth/create-auth.d.ts +51 -11
  9. package/dist/better-auth/create-auth.js +33 -11
  10. package/dist/better-auth/mcp/mcp-auth-plugins.d.ts +2719 -0
  11. package/dist/better-auth/mcp/mcp-auth-plugins.js +68 -0
  12. package/dist/better-auth/mcp/mcp-plugins.d.ts +7 -2
  13. package/dist/better-auth/mcp/mcp-plugins.js +3 -68
  14. package/dist/better-auth/mcp/mcp-token-claims.js +1 -1
  15. package/dist/better-auth/organizations/store.d.ts +1 -0
  16. package/dist/better-auth/saml-plugin.d.ts +993 -0
  17. package/dist/better-auth/saml-plugin.js +36 -0
  18. package/dist/better-auth/saml-sign-in.d.ts +27 -1
  19. package/dist/better-auth/saml-sign-in.js +1 -33
  20. package/dist/better-auth/secret-versions.d.ts +7 -0
  21. package/dist/better-auth/secret-versions.js +21 -0
  22. package/dist/better-auth/signing-keys.js +86 -0
  23. package/dist/better-auth/tokens.js +1 -0
  24. package/dist/better-auth/user-table.js +1 -1
  25. package/dist/better-auth/users.d.ts +1 -1
  26. package/dist/create-admin.d.ts +5 -3
  27. package/dist/create-admin.js +3 -1
  28. package/dist/encryption/keyring.js +1 -1
  29. package/dist/files/options.d.ts +5 -8
  30. package/dist/files/provider-settings.d.ts +16 -0
  31. package/dist/files/provider-settings.js +17 -0
  32. package/dist/files/runtime.js +4 -4
  33. package/dist/forms/runtime.js +3 -3
  34. package/dist/gotrue/sessions-plugin.js +3 -9
  35. package/dist/index.d.ts +16 -15
  36. package/dist/index.js +8 -10
  37. package/dist/internal.d.ts +13 -0
  38. package/dist/internal.js +13 -0
  39. package/dist/jobs/declared-job.d.ts +24 -0
  40. package/dist/jobs/declared-job.js +26 -0
  41. package/dist/jobs/job-types.d.ts +1 -1
  42. package/dist/mcp/mcp-authenticator.js +3 -3
  43. package/dist/notifications/device-store-of.d.ts +1 -1
  44. package/dist/notifications/device-store-of.js +1 -1
  45. package/dist/notifications/device-store.d.ts +1 -1
  46. package/dist/notifications/device-store.js +1 -1
  47. package/dist/notifications/history-store-of.d.ts +1 -1
  48. package/dist/notifications/history-store-of.js +1 -1
  49. package/dist/notifications/history-store.d.ts +1 -1
  50. package/dist/notifications/history-store.js +1 -1
  51. package/dist/notifications/options.d.ts +3 -1
  52. package/dist/notifications/options.js +1 -1
  53. package/dist/notifications/schema.d.ts +2 -2
  54. package/dist/notifications/schema.js +7 -7
  55. package/dist/notifications/send-notification.js +1 -1
  56. package/dist/notifications/sender.d.ts +1 -1
  57. package/dist/notifications/sender.js +2 -2
  58. package/dist/notifications/senders-of.d.ts +1 -1
  59. package/dist/notifications/senders-of.js +1 -1
  60. package/dist/package.js +1 -1
  61. package/dist/webhooks/subscription.d.ts +1 -0
  62. package/dist/webhooks/table/options.d.ts +1 -1
  63. package/dist/webhooks/table/schema.d.ts +2 -2
  64. package/dist/webhooks/table/schema.js +2 -2
  65. package/dist/webhooks/table/webhook-table.js +2 -2
  66. package/dist/webhooks/table/webhooks-module.d.ts +108 -0
  67. package/dist/webhooks/table/webhooks-module.js +11 -0
  68. package/package.json +6 -5
  69. package/dist/notifications/plugin.d.ts +0 -13
  70. package/dist/notifications/plugin.js +0 -16
  71. package/dist/project-config.d.ts +0 -54
  72. package/dist/project-config.js +0 -182
  73. package/dist/webhooks/table/plugin.d.ts +0 -10
  74. package/dist/webhooks/table/plugin.js +0 -18
@@ -0,0 +1,43 @@
1
+ import { buildAuth } from "./create-auth.js";
2
+ import { PostgresDialect } from "kysely";
3
+ //#region src/better-auth/auth-settings.ts
4
+ const organizationOptions = (organizations, hooks, app) => {
5
+ if (!organizations) return void 0;
6
+ const own = hooks.organizationHooks ?? {};
7
+ const betterAuthHooks = Object.fromEntries(Object.entries(own).map(([name, hook]) => [name, (input) => hook(input, app)]));
8
+ return {
9
+ ...organizations === true ? {} : organizations,
10
+ ...hooks.generateOrganizationId && { generateId: () => hooks.generateOrganizationId(app) },
11
+ ...hooks.onOrganizationCreated && { onCreated: (input) => hooks.onOrganizationCreated(input, app) },
12
+ ...hooks.beforeOrganizationDeleted && { beforeDelete: (organization) => hooks.beforeOrganizationDeleted(organization, app) },
13
+ ...Object.keys(betterAuthHooks).length > 0 && { hooks: betterAuthHooks }
14
+ };
15
+ };
16
+ /** createAuth's options for the `auth` settings, the store in the app's own database. */
17
+ const appAuthOptions = (settings, { pool, db, hooks = {}, mailer }) => {
18
+ const { schema = "auth", secret, generateUserId, mailer: mail, organizations, mcp, ...rest } = settings;
19
+ if (!secret) throw new Error("the auth secret is not set: give it the app's secret, PROTOBASE_SECRET, made with `openssl rand -base64 32`");
20
+ const app = { db };
21
+ const organizationsOptions = organizationOptions(organizations, hooks, app);
22
+ const userId = hooks.generateUserId ? () => hooks.generateUserId(app) : generateUserId;
23
+ const onUserCreated = hooks.onUserCreated && ((user, context) => hooks.onUserCreated(user, context, app));
24
+ return {
25
+ ...rest,
26
+ database: {
27
+ dialect: new PostgresDialect({ pool }),
28
+ type: "postgres",
29
+ schemaName: schema,
30
+ transaction: true
31
+ },
32
+ secret,
33
+ ...userId && { generateUserId: userId },
34
+ ...onUserCreated && { onUserCreated },
35
+ ...mail === false ? { mailer: false } : mailer && { mailer },
36
+ ...organizationsOptions && { organizations: organizationsOptions },
37
+ ...mcp && { mcp }
38
+ };
39
+ };
40
+ /** The app's Better Auth from its `auth` settings, built at serve time with only the plugins they turn on. */
41
+ const buildAppAuth = (settings, input) => buildAuth(appAuthOptions(settings, input));
42
+ //#endregion
43
+ export { appAuthOptions, buildAppAuth };
@@ -0,0 +1,15 @@
1
+ import { TenantValue } from "../types.js";
2
+ import { BetterAuthAuthenticator, BetterAuthAuthenticatorOptions } from "./authenticator.js";
3
+ import { AdminAuth } from "./create-auth.js";
4
+ //#region src/better-auth/authenticate-settings.d.ts
5
+ /**
6
+ * `authenticate` in `protobase.config.ts`: how the API verifies the app's tokens, as data. Without it, the tokens of
7
+ * the app's own `auth`; `jwksUrl` and `issuer` verify those of an auth server elsewhere instead.
8
+ */
9
+ export type AuthenticateSettings = Omit<BetterAuthAuthenticatorOptions, 'auth' | 'tenant'> & {
10
+ /** The one tenant everyone works in, for an app without organizations; never read from the token. */
11
+ tenant?: TenantValue;
12
+ };
13
+ /** The authenticator of the `authenticate` settings, over the app's Better Auth when it has one. */
14
+ export declare const buildAuthenticator: (settings?: AuthenticateSettings, auth?: AdminAuth) => BetterAuthAuthenticator;
15
+ //#endregion
@@ -0,0 +1,12 @@
1
+ import { betterAuthAuthenticator } from "./authenticator.js";
2
+ //#region src/better-auth/authenticate-settings.ts
3
+ /** The authenticator of the `authenticate` settings, over the app's Better Auth when it has one. */
4
+ const buildAuthenticator = (settings = {}, auth) => {
5
+ if (!auth && !settings.jwksUrl) throw new Error("No authenticator: give the config `auth`, or `authenticate.jwksUrl` and `issuer` for an auth server elsewhere; see https://docs.protobase.net/reference/auth/");
6
+ return betterAuthAuthenticator({
7
+ ...settings,
8
+ ...auth && { auth }
9
+ });
10
+ };
11
+ //#endregion
12
+ export { buildAuthenticator };
@@ -1,19 +1,22 @@
1
1
  import { UserColumn, UserTableOptions } from "./user-table.js";
2
2
  import { Mailer } from "../mail/smtp-mailer.js";
3
3
  import { SignInPolicy } from "./sign-in-policy.js";
4
- import { OperatorProvider } from "./operator-provider.js";
5
- import { PlatformSignIn } from "./platform-sign-in.js";
6
- import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js";
7
- import { OrganizationRole } from "./organizations/store.js";
8
- import { OrganizationsOptions, ResolvedOrganizations } from "./organizations/options.js";
9
4
  import { StaffSignIn } from "./staff-log.js";
10
- import "../index.js";
5
+ import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js";
11
6
  import { ConnectedApp } from "./mcp/connected-apps.js";
12
7
  import { ReplacingContext } from "./end-replaced-session.js";
8
+ import { VersionedSecret } from "./secret-versions.js";
13
9
  import { UserDeletionListener } from "./user-deletion.js";
14
- import "./auth-base-path.js";
10
+ import { OrganizationRole } from "./organizations/store.js";
11
+ import { OrganizationsOptions, ResolvedOrganizations } from "./organizations/options.js";
15
12
  import { McpAuthOptions } from "./mcp/mcp-plugins.js";
13
+ import { OperatorProvider } from "./operator-provider.js";
14
+ import { PlatformSignIn } from "./platform-sign-in.js";
15
+ import "../index.js";
16
+ import "./auth-base-path.js";
17
+ import { mcpAuthPlugins } from "./mcp/mcp-auth-plugins.js";
16
18
  import { SsoOptions } from "./saml-sign-in.js";
19
+ import { samlPlugin } from "./saml-plugin.js";
17
20
  import { signUpPaths } from "./sign-up/sign-up-plugin.js";
18
21
  import { PasswordHash } from "./password-hashes.js";
19
22
  import { BetterAuthOptions } from "better-auth";
@@ -46,6 +49,15 @@ export type CreateAuthOptions = {
46
49
  appName?: string;
47
50
  /** Signs cookies and encrypts the JWT private keys. Generate with `openssl rand -base64 32`. */
48
51
  secret: string;
52
+ /**
53
+ * The secrets the app had before `secret`, newest first, such as `PROTOBASE_PREVIOUS_SECRETS`: what they encrypted
54
+ * still decrypts, so the signing keys keep signing after the secret changes. Default none.
55
+ */
56
+ previousSecrets?: string[];
57
+ /** Days a token signing key signs before a new one takes over; default 30. */
58
+ signingKeyDays?: number;
59
+ /** Days a replaced signing key still verifies the tokens it signed; default 7, at least 1. */
60
+ signingKeyGraceDays?: number;
49
61
  /** Extra origins that may call the auth endpoints, such as the exact host of a dev tunnel. `baseURL` and `origins` are always trusted. */
50
62
  trustedOrigins?: string[];
51
63
  /**
@@ -169,7 +181,17 @@ export type UserCreatedHook = NonNullable<UserCreateHooks['after']>;
169
181
  */
170
182
  /** The configured roles with `admin` first; `admin` is added when missing. */
171
183
  export declare const normalizeRoles: (roles?: RolesInput) => string[];
172
- export declare const createAuth: (options: CreateAuthOptions) => import("better-auth").Auth<{
184
+ /**
185
+ * The plugins only some apps use, which `loadAuthPlugins` imports when the options turn them on: the SSO plugin of
186
+ * `sso.saml` and the OAuth provider of `mcp`.
187
+ */
188
+ export type AuthPlugins = {
189
+ samlPlugin?: typeof samlPlugin;
190
+ mcpAuthPlugins?: typeof mcpAuthPlugins;
191
+ };
192
+ /** The optional plugins `options` turns on, imported: an app without SAML or MCP loads neither. */
193
+ export declare const loadAuthPlugins: (options: Pick<CreateAuthOptions, "sso" | "mcp">) => Promise<AuthPlugins>;
194
+ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlugins) => import("better-auth").Auth<{
173
195
  user: {
174
196
  modelName: string;
175
197
  fields: {
@@ -473,9 +495,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
473
495
  expiresAt: Date;
474
496
  token: string;
475
497
  ipAddress?: string | null | undefined;
476
- userAgent?
477
- /** Sign-in attempts allowed per minute and client, default 5; also the sign-up requests allowed. */
478
- /** Sign-in attempts allowed per minute and client, default 5; also the sign-up requests allowed. */ : string | null | undefined;
498
+ userAgent?: string | null | undefined;
479
499
  };
480
500
  };
481
501
  }>>[];
@@ -1190,6 +1210,8 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1190
1210
  };
1191
1211
  };
1192
1212
  jwks: {
1213
+ rotationInterval: number;
1214
+ gracePeriod: number;
1193
1215
  keyPairConfigs: {
1194
1216
  readonly alg: "ES256";
1195
1217
  }[];
@@ -1452,6 +1474,21 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1452
1474
  };
1453
1475
  };
1454
1476
  };
1477
+ } | {
1478
+ id: "protobase-signing-keys";
1479
+ endpoints: {
1480
+ ensureSigningKey: import("better-call").StrictEndpoint<string, {
1481
+ method: "POST";
1482
+ }, {
1483
+ ok: boolean;
1484
+ }>;
1485
+ };
1486
+ hooks: {
1487
+ before: {
1488
+ matcher: (ctx: import("better-auth").HookEndpointContext) => boolean;
1489
+ handler: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<void>>;
1490
+ }[];
1491
+ };
1455
1492
  } | {
1456
1493
  hooks: {
1457
1494
  after: {
@@ -8777,6 +8814,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
8777
8814
  };
8778
8815
  basePath: string;
8779
8816
  secret: string;
8817
+ secrets: VersionedSecret[];
8780
8818
  trustedOrigins: string[] | ((request?: Request) => string[]);
8781
8819
  emailAndPassword: {
8782
8820
  onPasswordReset?: ((data: {
@@ -8868,4 +8906,6 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
8868
8906
  } | undefined;
8869
8907
  };
8870
8908
  export type AdminAuth = ReturnType<typeof createAuth>;
8909
+ /** `createAuth` with the optional plugins its options turn on loaded first (see `loadAuthPlugins`). */
8910
+ export declare const buildAuth: (options: CreateAuthOptions) => Promise<AdminAuth>;
8871
8911
  //#endregion
@@ -20,27 +20,29 @@ import { signUpPaths, signUpPlugin } from "./sign-up/sign-up-plugin.js";
20
20
  import { leakedPasswordsPlugin } from "./leaked-passwords.js";
21
21
  import { userDeletion } from "./user-deletion.js";
22
22
  import { revokeMcpGrants } from "./mcp/revoke-grants.js";
23
- import { coreNames, pluginNames } from "./snake-case-names.js";
23
+ import { mcpUnusedPaths, resolveMcp } from "./mcp/mcp-plugins.js";
24
24
  import { lastOrganizationField, sessionOrganizationHooks } from "./organizations/session-organization.js";
25
- import { tokenClaims } from "./token-claims.js";
26
- import { mcpAuthPlugins, mcpUnusedPaths, resolveMcp } from "./mcp/mcp-plugins.js";
27
25
  import { organizationPlugin } from "./organizations/organization-plugin.js";
28
26
  import { protobaseOrganizationPlugin } from "./organizations/protobase-plugin.js";
29
27
  import { resolveOrganizations } from "./organizations/options.js";
30
28
  import { passkeyHosts, readPasskeyDomain, resolvePasskeyDomain } from "./passkey-domain.js";
29
+ import { coreNames, pluginNames } from "./snake-case-names.js";
31
30
  import { passkeyPlugin } from "./passkey-plugin.js";
32
31
  import { passwordResetOptions } from "./password-reset.js";
33
32
  import { rolesPlugin } from "./roles/roles-plugin.js";
33
+ import { versionedSecrets } from "./secret-versions.js";
34
+ import { signingKeyRotation, signingKeys } from "./signing-keys.js";
34
35
  import { platformSignInFor, platformSignInProvider, readPlatformSignIn, resolvePlatformSignIn } from "./platform-sign-in.js";
35
36
  import { appSignInPolicy } from "./app-sign-in-policy.js";
36
37
  import { assertNoEndpointKeyClashes } from "./endpoint-key-clashes.js";
37
- import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin, samlPlugin } from "./saml-sign-in.js";
38
+ import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin } from "./saml-sign-in.js";
38
39
  import { noAccountMailPlugin } from "./no-account-mail.js";
39
40
  import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
40
41
  import { confirmedOnCreate, confirmedOnUpdate, emailConfirmedField } from "./email-confirmed.js";
41
42
  import { rehashPasswordsPlugin, verifyAcceptedHash } from "./password-hashes.js";
42
43
  import { staffSignInPlugin } from "./staff-plugin.js";
43
44
  import { strongSignInsPlugin } from "./strong-sign-ins.js";
45
+ import { tokenClaims } from "./token-claims.js";
44
46
  import { twoFactorAfterPasswordOrCode } from "./two-factor-challenge.js";
45
47
  import { metadataField, userFieldsNamed, userModel, userTable, withUserColumns } from "./user-table.js";
46
48
  import { usersPlugin } from "./users/users-plugin.js";
@@ -70,6 +72,15 @@ const tokenLifetime = "15m";
70
72
  /** The configured roles with `admin` first; `admin` is added when missing. */
71
73
  const normalizeRoles = (roles) => roleDefinitions(roles).names;
72
74
  const adminImpersonationPaths = ["/admin/impersonate-user", "/admin/stop-impersonating"];
75
+ /** The optional plugins `options` turns on, imported: an app without SAML or MCP loads neither. */
76
+ const loadAuthPlugins = async (options) => ({
77
+ ...(options.sso?.saml.length ?? 0) > 0 && { samlPlugin: (await import("./saml-plugin.js")).samlPlugin },
78
+ ...options.mcp && { mcpAuthPlugins: (await import("./mcp/mcp-auth-plugins.js")).mcpAuthPlugins }
79
+ });
80
+ const loaded = (plugin, option) => {
81
+ if (!plugin) throw new Error(`${option} needs its plugin loaded: build auth with buildAuth, which loads it`);
82
+ return plugin;
83
+ };
73
84
  const newUserId = (generate) => generate === "uuid" ? crypto.randomUUID() : generate();
74
85
  const noContext = () => Promise.reject(/* @__PURE__ */ new Error("The auth context is read only once createAuth has made the instance"));
75
86
  const organizationPaths = ["/organization/set-active", "/organization/accept-invitation"];
@@ -77,7 +88,7 @@ const organizationPaths = ["/organization/set-active", "/organization/accept-inv
77
88
  * The options `createAuth` gives Better Auth, with the roles, default role, mailer, operator provider and platform
78
89
  * sign-in provider already resolved.
79
90
  */
80
- const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, mcp, context = noContext, signInPolicy = defaultSignInPolicy, mobile, deletion }) => {
91
+ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, mcp, context = noContext, signInPolicy = defaultSignInPolicy, mobile, deletion, ...plugins }) => {
81
92
  const secure = options.baseURL.startsWith("https://");
82
93
  const signIn = platformSignInFor(platformSignIn, Object.keys(options.socialProviders ?? {}));
83
94
  const hosts = passkeyHosts([options.baseURL, ...origins]);
@@ -101,7 +112,10 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
101
112
  };
102
113
  const jwtOptions = {
103
114
  schema: pluginNames.jwt,
104
- jwks: { keyPairConfigs: [goTrueKeyPair] },
115
+ jwks: {
116
+ keyPairConfigs: [goTrueKeyPair],
117
+ ...signingKeyRotation(options)
118
+ },
105
119
  jwt: {
106
120
  expirationTime: tokenLifetime,
107
121
  issuer: options.baseURL,
@@ -121,6 +135,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
121
135
  },
122
136
  disableSettingJwtHeader: true
123
137
  };
138
+ const keys = signingKeys(jwtOptions);
139
+ const { secrets, legacySecret } = versionedSecrets(options.secret, options.previousSecrets);
124
140
  const trusted = [
125
141
  options.baseURL,
126
142
  ...origins,
@@ -137,7 +153,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
137
153
  logger: authLogger,
138
154
  baseURL: multiHostBaseURL(options.baseURL, origins),
139
155
  basePath: authBasePath,
140
- secret: options.secret,
156
+ secret: legacySecret,
157
+ secrets,
141
158
  trustedOrigins: mobile ? (request) => [...trusted, ...mobile.trusted(request)] : trusted,
142
159
  emailAndPassword: {
143
160
  enabled: true,
@@ -166,6 +183,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
166
183
  roles: Object.fromEntries(roles.map((role) => [role, role === "admin" ? adminAc : userAc]))
167
184
  }),
168
185
  jwt(jwtOptions),
186
+ keys.plugin,
169
187
  twoFactorAfterPasswordOrCode({
170
188
  schema: pluginNames.twoFactor,
171
189
  allowPasswordless: true,
@@ -185,13 +203,14 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
185
203
  defaultPolicy: signInPolicy
186
204
  })] : [],
187
205
  ...signIn ? [genericOAuth({ config: [platformSignInProvider(signIn)] })] : [],
188
- ...saml.length > 0 ? [samlPlugin(saml, options.baseURL), samlGuardPlugin(samlIds)] : [],
206
+ ...saml.length > 0 ? [loaded(plugins.samlPlugin, "sso.saml")(saml, options.baseURL), samlGuardPlugin(samlIds)] : [],
189
207
  signInPolicyPlugin(policy),
190
208
  setPasswordPlugin(),
191
209
  ...options.leakedPasswords ? [leakedPasswordsPlugin()] : [],
192
210
  goTrueSessionsPlugin({
193
211
  issuer: goTrueIssuer(options.baseURL),
194
212
  jwt: jwtOptions,
213
+ signingKey: keys.ensure,
195
214
  policy
196
215
  }),
197
216
  staffSignInPlugin({
@@ -220,7 +239,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
220
239
  ...organizations && { organizations }
221
240
  }),
222
241
  ...passwordHashes.length > 0 ? [rehashPasswordsPlugin(passwordHashes)] : [],
223
- ...mcp ? mcpAuthPlugins({
242
+ ...mcp ? loaded(plugins.mcpAuthPlugins, "mcp")({
224
243
  mcp,
225
244
  organizations: Boolean(organizations),
226
245
  mail: Boolean(mailer),
@@ -318,7 +337,7 @@ const checkSignUpRole = (role, roles) => {
318
337
  if (!roles.includes(role)) throw new Error(`signUp.role "${role}" is not one of the roles: ${roles.join(", ")}`);
319
338
  return role;
320
339
  };
321
- const createAuth = (options) => {
340
+ const createAuth = (options, plugins = {}) => {
322
341
  if (options.signUp && options.organizations) throw new Error("signUp is not available with organizations: their people join by invitation");
323
342
  const definitions = roleDefinitions(options.roles, { organizations: options.organizations !== void 0 });
324
343
  const roles = definitions.names;
@@ -344,6 +363,7 @@ const createAuth = (options) => {
344
363
  const mobile = mobileRequests();
345
364
  const deletion = userDeletion();
346
365
  const authOptions = betterAuthOptions(options, {
366
+ ...plugins,
347
367
  mobile,
348
368
  deletion,
349
369
  origins,
@@ -428,5 +448,7 @@ const createAuth = (options) => {
428
448
  }
429
449
  });
430
450
  };
451
+ /** `createAuth` with the optional plugins its options turn on loaded first (see `loadAuthPlugins`). */
452
+ const buildAuth = async (options) => createAuth(options, await loadAuthPlugins(options));
431
453
  //#endregion
432
- export { authBasePath, betterAuthOptions, createAuth, normalizeRoles };
454
+ export { authBasePath, betterAuthOptions, buildAuth, createAuth, loadAuthPlugins, normalizeRoles };