@protobase/server 0.67.4 → 0.67.5

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.
@@ -590,7 +590,17 @@ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlug
590
590
  userId: string;
591
591
  expiresAt: Date;
592
592
  token: string;
593
- ipAddress?: string | null | undefined;
593
+ ipAddress?
594
+ /**
595
+ * What the sign-in page leads with: `password`, the email address and a password, with emailing a code below it; or
596
+ * `emailCode`, the address and "Email me a sign-in code", with the password behind "Use a password", for apps whose
597
+ * people mostly never set one. Only while the policy leaves both on; default `password`.
598
+ */
599
+ /**
600
+ * What the sign-in page leads with: `password`, the email address and a password, with emailing a code below it; or
601
+ * `emailCode`, the address and "Email me a sign-in code", with the password behind "Use a password", for apps whose
602
+ * people mostly never set one. Only while the policy leaves both on; default `password`.
603
+ */ : string | null | undefined;
594
604
  userAgent?: string | null | undefined;
595
605
  };
596
606
  };
@@ -1666,7 +1676,7 @@ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlug
1666
1676
  email: string;
1667
1677
  emailVerified: boolean;
1668
1678
  name: string;
1669
- image? /** 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. */ : string | null | undefined;
1679
+ image?: string | null | undefined;
1670
1680
  };
1671
1681
  };
1672
1682
  }>>[];
@@ -2411,9 +2421,7 @@ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlug
2411
2421
  email: string;
2412
2422
  emailVerified: boolean;
2413
2423
  name: string;
2414
- image?
2415
- /** The RP ID of new passkeys, from `resolvePasskeyDomain`; default the host of `baseURL`. */
2416
- /** The RP ID of new passkeys, from `resolvePasskeyDomain`; default the host of `baseURL`. */ : string | null | undefined;
2424
+ image?: string | null | undefined;
2417
2425
  };
2418
2426
  };
2419
2427
  }>>[];
@@ -17,7 +17,7 @@ import { jsonOnlyOnMobile, mobileRequests } from "./mobile-requests.js";
17
17
  import { passwordLength } from "./new-account-password.js";
18
18
  import { platformMailer } from "../mail/platform-mailer.js";
19
19
  import { readOperatorSettings, resolveOperator } from "./operator-provider.js";
20
- import { defaultInvitationUrl } from "./invitations/invitation-link-hook.js";
20
+ import "./invitations/invitation-link-hook.js";
21
21
  import { lastAdminGuardPlugin } from "./last-admin-guard.js";
22
22
  import { setPasswordPath, setPasswordPlugin } from "./set-password.js";
23
23
  import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
@@ -271,7 +271,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
271
271
  definitions,
272
272
  defaultPolicy: signInPolicy,
273
273
  ...mail && { mail },
274
- invitationUrl: organizations?.invitationUrl ?? defaultInvitationUrl(options.baseURL),
274
+ ...organizations?.invitationUrl !== void 0 && { invitationUrl: organizations.invitationUrl },
275
275
  invitationSeconds: organizations?.invitationExpiresIn ?? 604800,
276
276
  ...organizations && { organizations }
277
277
  }),
@@ -410,7 +410,7 @@ const createAuth = (options, plugins = {}) => {
410
410
  const personalMail = resolvePersonalMail(options.personalMail ?? [], socialProviders, options.encryptOAuthTokens === true);
411
411
  const origins = resolveOrigins(options.baseURL, options.origins ?? platformOrigins());
412
412
  const passkeyDomain = resolvePasskeyDomain(options.passkeyDomain ?? platformPasskeyDomain(), [options.baseURL, ...origins]);
413
- const organizations = options.organizations && resolveOrganizations(options.organizations, definitions, options.baseURL);
413
+ const organizations = options.organizations && resolveOrganizations(options.organizations, definitions);
414
414
  const mcp = options.mcp && resolveMcp(options.baseURL, options.mcp);
415
415
  let instance;
416
416
  const context = () => instance?.$context ?? Promise.reject(/* @__PURE__ */ new Error("The auth instance is not made yet"));
@@ -1,3 +1,4 @@
1
+ import { requestAppUrl } from "../../request-app-url.js";
1
2
  import { invitationLink, invitationToken } from "./invitation-token.js";
2
3
  import { createAuthMiddleware } from "better-auth/api";
3
4
  //#region src/better-auth/invitations/invitation-link-hook.ts
@@ -9,7 +10,7 @@ import { createAuthMiddleware } from "better-auth/api";
9
10
  const invitationLinkHook = (paths, invitationUrl) => ({
10
11
  matcher: (context) => paths.includes(context.path ?? ""),
11
12
  handler: createAuthMiddleware(async (ctx) => {
12
- const answer = await withInvitationLink(ctx.context.returned, ctx.context.secret, invitationUrl);
13
+ const answer = await withInvitationLink(ctx.context.returned, ctx.context.secret, invitationPage(invitationUrl, ctx.context.baseURL));
13
14
  if (answer) ctx.context.returned = answer;
14
15
  })
15
16
  });
@@ -26,9 +27,13 @@ const withInvitationLink = async (returned, secret, invitationUrl) => {
26
27
  link: invitationLink(invitationUrl, token)
27
28
  };
28
29
  };
29
- /** The page an invitation link opens, with `?token=`, unless the app names another. */
30
- const defaultInvitationUrl = (baseURL) => `${baseURL.replace(/\/$/, "")}/-/invitation`;
30
+ /**
31
+ * The page an invitation link opens, with `?token=`: the app's own `invitationUrl`, or `/-/invitation` on the origin
32
+ * the request came to, from Better Auth's `ctx.context.baseURL` (`requestAppUrl`), so a link made on one of the app's
33
+ * origins opens there.
34
+ */
35
+ const invitationPage = (invitationUrl, requestURL) => invitationUrl ?? requestAppUrl(requestURL, "/-/invitation");
31
36
  /** How long an invitation works unless the app says otherwise: 7 days, in seconds. */
32
37
  const defaultInvitationSeconds = 604800;
33
38
  //#endregion
34
- export { defaultInvitationSeconds, defaultInvitationUrl, invitationLinkHook, withInvitationLink };
39
+ export { defaultInvitationSeconds, invitationLinkHook, invitationPage, withInvitationLink };
@@ -22,7 +22,7 @@ export type OrganizationsOptions = {
22
22
  * rows in any `.tenant` resource; `'cascade'` leaves it to the project's foreign keys.
23
23
  */
24
24
  deleteRecords?: 'refuse' | 'cascade';
25
- /** The page an invitation link opens, with `?token=`. Default `{baseURL}/-/invitation`. */
25
+ /** The page an invitation link opens, with `?token=`. Default `/-/invitation` on the origin the request came to. */
26
26
  invitationUrl?: string;
27
27
  /** Runs after an organization is created, with its owner's membership, for example to add the app's own row for it. */
28
28
  onCreated?: (input: {
@@ -42,7 +42,7 @@ export type ResolvedOrganizations = {
42
42
  membershipLimit: number;
43
43
  generateId: () => string | Promise<string>;
44
44
  deleteRecords: 'refuse' | 'cascade';
45
- invitationUrl: string;
45
+ invitationUrl?: string;
46
46
  options: OrganizationsOptions;
47
47
  };
48
48
  //#endregion
@@ -1,7 +1,6 @@
1
1
  import { membershipRoles } from "./role-definitions.js";
2
- import { defaultInvitationUrl } from "../invitations/invitation-link-hook.js";
3
2
  //#region src/better-auth/organizations/options.ts
4
- const resolveOrganizations = (options, definitions, baseURL) => {
3
+ const resolveOrganizations = (options, definitions) => {
5
4
  const holdable = membershipRoles(definitions);
6
5
  const creatorAppRoles = options.creatorAppRoles === void 0 || options.creatorAppRoles === "all" ? holdable : options.creatorAppRoles;
7
6
  const unknown = creatorAppRoles.find((name) => !holdable.includes(name));
@@ -16,7 +15,7 @@ const resolveOrganizations = (options, definitions, baseURL) => {
16
15
  membershipLimit: options.membershipLimit ?? 1e3,
17
16
  generateId: options.generateId ?? (() => crypto.randomUUID()),
18
17
  deleteRecords: options.deleteRecords ?? "refuse",
19
- invitationUrl: options.invitationUrl ?? defaultInvitationUrl(baseURL),
18
+ ...options.invitationUrl !== void 0 && { invitationUrl: options.invitationUrl },
20
19
  options
21
20
  };
22
21
  };
@@ -2,8 +2,10 @@ import "./role-definitions.js";
2
2
  import { checkAppRoleChange, effectiveRoles, globalRoles, parseAppRoles } from "./app-roles.js";
3
3
  import { findMember } from "./store.js";
4
4
  import { invitationLink, invitationToken } from "../invitations/invitation-token.js";
5
+ import { invitationPage } from "../invitations/invitation-link-hook.js";
5
6
  import { sendAuthEmail } from "../auth-mail/send-auth-email.js";
6
7
  import { APIError } from "better-auth/api";
8
+ import { getCurrentAuthEndpointContext } from "@better-auth/core/context";
7
9
  import { organization } from "better-auth/plugins/organization";
8
10
  //#region src/better-auth/organizations/organization-plugin.ts
9
11
  /** The snake_case names of the organization plugin's tables and columns, with the app roles of members and invitations. */
@@ -112,8 +114,8 @@ const organizationPlugin = ({ resolved, secret, mail, context }) => {
112
114
  cancelPendingInvitationsOnReInvite: true,
113
115
  schema: organizationNames,
114
116
  ...mail && { sendInvitationEmail: async ({ invitation, organization, inviter }) => {
115
- const token = await invitationToken(secret, invitation);
116
- const url = invitationLink(resolved.invitationUrl, token);
117
+ const page = invitationPage(resolved.invitationUrl, getCurrentAuthEndpointContext().context.baseURL);
118
+ const url = invitationLink(page, await invitationToken(secret, invitation));
117
119
  await sendAuthEmail(mail, "invitationEmail", invitation.email, {
118
120
  inviter: inviter.user.name || inviter.user.email,
119
121
  organization: organization.name,
@@ -1,5 +1,6 @@
1
1
  import { adminOnly } from "../admin-only.js";
2
2
  import { invitationLink, invitationToken } from "../invitations/invitation-token.js";
3
+ import { invitationPage } from "../invitations/invitation-link-hook.js";
3
4
  import { sendAuthEmail } from "../auth-mail/send-auth-email.js";
4
5
  import { createAppInvitation, deleteAppInvitation, listAppInvitations } from "./app-invitation-store.js";
5
6
  import { roleLabels } from "../roles/role-labels.js";
@@ -46,7 +47,7 @@ const userEndpoints = ({ definitions, mail, invitationUrl, invitationSeconds })
46
47
  inviterId: inviter.id
47
48
  }, invitationSeconds);
48
49
  if (mail) {
49
- const url = invitationLink(invitationUrl, await invitationToken(ctx.context.secret, invitation));
50
+ const url = invitationLink(invitationPage(invitationUrl, ctx.context.baseURL), await invitationToken(ctx.context.secret, invitation));
50
51
  await sendAuthEmail(mail, "invitationEmail", invitation.email, {
51
52
  inviter: inviter.name || inviter.email,
52
53
  url,
@@ -1,3 +1,4 @@
1
+ import { requestAppUrl } from "../request-app-url.js";
1
2
  //#region src/mail/mailing-app.ts
2
3
  /**
3
4
  * The app as an auth email names it: `appName`, and the home page on the origin of `requestURL`, a URL on the origin
@@ -5,7 +6,7 @@
5
6
  */
6
7
  const mailingApp = ({ appName, baseURL }, requestURL) => ({
7
8
  name: appName,
8
- url: new URL("/", requestURL ?? baseURL).href
9
+ url: requestAppUrl(requestURL ?? baseURL, "/")
9
10
  });
10
11
  //#endregion
11
12
  export { mailingApp };
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.67.4";
2
+ var version = "0.67.5";
3
3
  //#endregion
4
4
  export { version };
@@ -0,0 +1,9 @@
1
+ //#region src/request-app-url.ts
2
+ /**
3
+ * The app's page at `path`, which starts with `/`, on the origin of `requestURL`: a URL Better Auth resolved for the
4
+ * request (`ctx.context.baseURL`), on the request's host when that is one of the app's origins and on `baseURL`'s
5
+ * otherwise. Only its origin counts, so Better Auth's base path, such as `/api/auth`, never reaches the page.
6
+ */
7
+ const requestAppUrl = (requestURL, path) => new URL(path, requestURL).href;
8
+ //#endregion
9
+ export { requestAppUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.67.4",
3
+ "version": "0.67.5",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -37,15 +37,16 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@better-auth/cimd": "^1.7.12",
40
+ "@better-auth/core": "^1.7.12",
40
41
  "@better-auth/mcp": "^1.7.12",
41
42
  "@better-auth/oauth-provider": "^1.7.12",
42
43
  "@better-auth/passkey": "^1.7.12",
43
44
  "@better-auth/sso": "^1.7.12",
44
45
  "@livekit/protocol": "1.53.1",
45
46
  "@modelcontextprotocol/server": "^2.3.1",
46
- "@protobase/layout": "0.67.4",
47
- "@protobase/query": "0.67.4",
48
- "@protobase/schema": "0.67.4",
47
+ "@protobase/layout": "0.67.5",
48
+ "@protobase/query": "0.67.5",
49
+ "@protobase/schema": "0.67.5",
49
50
  "@react-email/render": "^2.1.0",
50
51
  "@react-pdf/primitives": "^4.4.0",
51
52
  "@react-pdf/renderer": "^4.9.0",