@protobase/server 0.66.1 → 0.66.2

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.
@@ -8714,6 +8714,53 @@ export declare const buildAppAuth: (settings: AuthSettings, input: AppAuthInput)
8714
8714
  }, ctx: ReplacingContext | null) => Promise<void>;
8715
8715
  };
8716
8716
  };
8717
+ account: {
8718
+ create?: {
8719
+ before?: (account: import("better-auth").Account, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void | {
8720
+ data: {
8721
+ id?: string | undefined;
8722
+ createdAt?: Date | undefined;
8723
+ updatedAt?: Date | undefined;
8724
+ providerId?: string | undefined;
8725
+ accountId?: string | undefined;
8726
+ userId?: string | undefined;
8727
+ accessToken?: string | null | undefined;
8728
+ refreshToken?: string | null | undefined;
8729
+ idToken?: string | null | undefined;
8730
+ accessTokenExpiresAt?: Date | null | undefined;
8731
+ refreshTokenExpiresAt?: Date | null | undefined;
8732
+ scope?: string | null | undefined;
8733
+ password?: string | null | undefined;
8734
+ } & Record<string, any>;
8735
+ }>;
8736
+ after?: (account: import("better-auth").Account, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8737
+ };
8738
+ update?: {
8739
+ before?: (account: Partial<import("better-auth").Account> & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void | {
8740
+ data: {
8741
+ [x: string]: any;
8742
+ id?: string | undefined;
8743
+ createdAt?: Date | undefined;
8744
+ updatedAt?: Date | undefined;
8745
+ providerId?: string | undefined;
8746
+ accountId?: string | undefined;
8747
+ userId?: string | undefined;
8748
+ accessToken?: string | null | undefined;
8749
+ refreshToken?: string | null | undefined;
8750
+ idToken?: string | null | undefined;
8751
+ accessTokenExpiresAt?: Date | null | undefined;
8752
+ refreshTokenExpiresAt?: Date | null | undefined;
8753
+ scope?: string | null | undefined;
8754
+ password?: string | null | undefined;
8755
+ };
8756
+ }>;
8757
+ after?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8758
+ };
8759
+ delete?: {
8760
+ before?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void>;
8761
+ after?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8762
+ };
8763
+ };
8717
8764
  };
8718
8765
  rateLimit: {
8719
8766
  enabled: true;
@@ -8832,6 +8832,53 @@ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlug
8832
8832
  }, ctx: ReplacingContext | null) => Promise<void>;
8833
8833
  };
8834
8834
  };
8835
+ account: {
8836
+ create?: {
8837
+ before?: (account: import("better-auth").Account, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void | {
8838
+ data: {
8839
+ id?: string | undefined;
8840
+ createdAt?: Date | undefined;
8841
+ updatedAt?: Date | undefined;
8842
+ providerId?: string | undefined;
8843
+ accountId?: string | undefined;
8844
+ userId?: string | undefined;
8845
+ accessToken?: string | null | undefined;
8846
+ refreshToken?: string | null | undefined;
8847
+ idToken?: string | null | undefined;
8848
+ accessTokenExpiresAt?: Date | null | undefined;
8849
+ refreshTokenExpiresAt?: Date | null | undefined;
8850
+ scope?: string | null | undefined;
8851
+ password?: string | null | undefined;
8852
+ } & Record<string, any>;
8853
+ }>;
8854
+ after?: (account: import("better-auth").Account, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8855
+ };
8856
+ update?: {
8857
+ before?: (account: Partial<import("better-auth").Account> & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void | {
8858
+ data: {
8859
+ [x: string]: any;
8860
+ id?: string | undefined;
8861
+ createdAt?: Date | undefined;
8862
+ updatedAt?: Date | undefined;
8863
+ providerId?: string | undefined;
8864
+ accountId?: string | undefined;
8865
+ userId?: string | undefined;
8866
+ accessToken?: string | null | undefined;
8867
+ refreshToken?: string | null | undefined;
8868
+ idToken?: string | null | undefined;
8869
+ accessTokenExpiresAt?: Date | null | undefined;
8870
+ refreshTokenExpiresAt?: Date | null | undefined;
8871
+ scope?: string | null | undefined;
8872
+ password?: string | null | undefined;
8873
+ };
8874
+ }>;
8875
+ after?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8876
+ };
8877
+ delete?: {
8878
+ before?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<boolean | void>;
8879
+ after?: (account: import("better-auth").Account & Record<string, unknown>, context: import("better-auth").GenericEndpointContext | null) => Promise<void>;
8880
+ };
8881
+ };
8835
8882
  };
8836
8883
  rateLimit: {
8837
8884
  enabled: true;
@@ -8,6 +8,7 @@ import { goTrueSessionsPlugin } from "../gotrue/sessions-plugin.js";
8
8
  import { betterAuthLinkedAccounts } from "../personal-mail/linked-accounts.js";
9
9
  import { resolvePersonalMail } from "../personal-mail/personal-mail-providers.js";
10
10
  import { personalMailer } from "../personal-mail/personal-mailer.js";
11
+ import { mailLinkHooks } from "../personal-mail/mail-link-hooks.js";
11
12
  import { endReplacedSession } from "./end-replaced-session.js";
12
13
  import { authBasePath } from "./auth-base-path.js";
13
14
  import { authLogger } from "./auth-logger.js";
@@ -299,7 +300,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
299
300
  },
300
301
  update: { before: async (user) => ({ data: confirmedOnUpdate(user, /* @__PURE__ */ new Date()) }) }
301
302
  },
302
- session: withEndedReplaced(organizations ? sessionOrganizationHooks(context) : {})
303
+ session: withEndedReplaced(organizations ? sessionOrganizationHooks(context) : {}),
304
+ account: mailLinkHooks(resolvePersonalMail(options.personalMail ?? [], Object.keys(options.socialProviders ?? {}), options.encryptOAuthTokens === true))
303
305
  },
304
306
  rateLimit: {
305
307
  enabled: true,
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.66.1";
2
+ var version = "0.66.2";
3
3
  //#endregion
4
4
  export { version };
@@ -12,7 +12,8 @@ const betterAuthLinkedAccounts = (auth) => ({
12
12
  return (await (await auth.$context).internalAdapter.findAccounts(userId)).map((account) => ({
13
13
  id: account.id,
14
14
  providerId: account.providerId,
15
- scopes: storedScopes(account.scope)
15
+ scopes: storedScopes(account.scope),
16
+ createdAt: account.createdAt
16
17
  }));
17
18
  },
18
19
  accessToken: async (userId, account, provider) => {
@@ -33,4 +34,4 @@ const validAccessToken = async (auth, userId, accountId, provider) => {
33
34
  }
34
35
  };
35
36
  //#endregion
36
- export { betterAuthLinkedAccounts };
37
+ export { betterAuthLinkedAccounts, storedScopes };
@@ -0,0 +1,34 @@
1
+ import { storedScopes } from "./linked-accounts.js";
2
+ import { getOAuthState } from "better-auth/api";
3
+ //#region src/personal-mail/mail-link-hooks.ts
4
+ const holds = (scope, scopes) => scopes.every((wanted) => storedScopes(scope).includes(wanted));
5
+ /** The mail provider whose scopes `account` holds, of the app's `providers`. */
6
+ const mailProviderOf = (providers, account) => providers.find((candidate) => candidate.signInProvider === account.providerId && holds(account.scope, candidate.scopes));
7
+ /**
8
+ * The new scopes of `userAccounts` once `linked` holds `provider`'s mail scopes: each other account of its sign-in
9
+ * provider that holds them too loses them, so mail goes through `linked` alone.
10
+ */
11
+ const scopesHandedOver = (provider, linked, userAccounts) => userAccounts.filter((account) => account.id !== linked.id && account.providerId === linked.providerId && holds(account.scope, provider.scopes)).map((account) => ({
12
+ id: account.id,
13
+ scope: storedScopes(account.scope).filter((scope) => !provider.scopes.includes(scope)).join(",") || null
14
+ }));
15
+ /**
16
+ * Better Auth's `databaseHooks.account`: an account written by a link, whose OAuth state names the person it links to,
17
+ * becomes the one mail goes through when it holds a mail provider's scopes (see `scopesHandedOver`); other writes read nothing. Only a link
18
+ * writes an existing account's scopes: a sign-in or a token refresh rewrites its tokens and leaves its scopes as they
19
+ * were, so signing in with an older account does not take the mail back to it.
20
+ */
21
+ const mailLinkHooks = (providers) => {
22
+ const handOver = async (account, context) => {
23
+ const provider = mailProviderOf(providers, account);
24
+ if (!context || !provider || !(await getOAuthState())?.link) return;
25
+ const adapter = context.context.internalAdapter;
26
+ for (const { id, scope } of scopesHandedOver(provider, account, await adapter.findAccounts(account.userId))) await adapter.updateAccount(id, { scope });
27
+ };
28
+ return {
29
+ create: { after: handOver },
30
+ update: { after: handOver }
31
+ };
32
+ };
33
+ //#endregion
34
+ export { mailLinkHooks, mailProviderOf, scopesHandedOver };
@@ -1,10 +1,14 @@
1
1
  import { PersonalMailError } from "./personal-mail-error.js";
2
2
  //#region src/personal-mail/personal-mailer.ts
3
- /** The mail provider `userId` linked, of the app's `providers` in their order, with the account that holds its tokens. */
3
+ /**
4
+ * The mail provider `userId` linked, of the app's `providers` in their order, with the account that holds its tokens.
5
+ * Linking a mailbox takes the mail scopes off the person's other accounts of that provider (`mailLinkHooks`), so one
6
+ * account holds them; of accounts that still both hold them, as when both were linked before that, the one created last.
7
+ */
4
8
  const linkedProvider = async (accounts, providers, userId) => {
5
9
  const linked = await accounts.accounts(userId);
6
10
  for (const provider of providers) {
7
- const account = linked.find((candidate) => candidate.providerId === provider.signInProvider && provider.scopes.every((scope) => candidate.scopes.includes(scope)));
11
+ const account = linked.filter((candidate) => candidate.providerId === provider.signInProvider && provider.scopes.every((scope) => candidate.scopes.includes(scope))).reduce((latest, candidate) => latest && latest.createdAt >= candidate.createdAt ? latest : candidate, void 0);
8
12
  if (account) return {
9
13
  provider,
10
14
  account
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.66.1",
3
+ "version": "0.66.2",
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": {
@@ -42,9 +42,9 @@
42
42
  "@better-auth/sso": "^1.7.12",
43
43
  "@livekit/protocol": "1.53.1",
44
44
  "@modelcontextprotocol/server": "^2.3.1",
45
- "@protobase/layout": "0.66.1",
46
- "@protobase/query": "0.66.1",
47
- "@protobase/schema": "0.66.1",
45
+ "@protobase/layout": "0.66.2",
46
+ "@protobase/query": "0.66.2",
47
+ "@protobase/schema": "0.66.2",
48
48
  "@react-email/render": "^2.1.0",
49
49
  "@scalar/hono-api-reference": "^0.12.10",
50
50
  "@sentry/core": "^11.7.0",