@sanoma-ai/connector-better-auth 0.2.3 → 0.2.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.
package/README.md CHANGED
@@ -35,7 +35,7 @@ It has no credential. Better Auth signs its cookies with a key the app derives f
35
35
 
36
36
  ### The schema
37
37
 
38
- The tables (`user`, `session`, `account`, `verification`) are in the schema `better_auth` on the company's database (`SANOMA_DATABASE_URL`), beside the runtime's own, in queries that name the schema (no `search_path`, which the URL's own `options` could change). The login runs Better Auth's migrations when the app starts, before it serves the site: once per database in a process, under a Postgres advisory lock, so several processes starting at once do not race. A second start changes nothing.
38
+ The tables (`user`, `session`, `account`, `verification`, and `rateLimit`, the rate limit's counts) are in the schema `better_auth` on the company's database (`SANOMA_DATABASE_URL`), beside the runtime's own, in queries that name the schema (no `search_path`, which the URL's own `options` could change), reached through the pool the app gives the login (`LoginContext.database`: the app's one pool on Node, the request's own on a Worker, with one Better Auth instance per pool and nothing else kept across requests). The worker makes them at its start, as it makes the runtime's own tables: `prepareBetterAuth`, the connector's `prepare`, runs Better Auth's migrations on the database's direct URL under a Postgres advisory lock, so two workers starting at once do not race, and a second run changes nothing; the `sanoma` bin's worker runs `prepareBetterAuthStaff` the same way for the staff sign-in's schema, which no config names. The app never migrates: a login whose first request finds a database the worker has not prepared yet, or has prepared for an older version, waits a minute for a worker starting beside the app, then refuses, saying so (start the worker first, or with it). Nothing of the session is cached in the cookie: every request reads its session row, so a revoked session is gone at once.
39
39
 
40
40
  ### The routes
41
41
 
@@ -58,7 +58,7 @@ const response = await fetch("/auth/better-auth/sign-in/email", {
58
58
  if (response.ok) window.location.reload(); // the app reads the new session cookie
59
59
  ```
60
60
 
61
- The session is one cookie, `better-auth.session_token` (`__Secure-better-auth.session_token`, and `Secure`, when the site is served over https): HttpOnly, SameSite=Lax, for the site's host only (no `Domain`), for seven days from sign-in. A request for a host that is not the site's is answered 421. Sign-in and sign-up are rate limited per visitor address (three tries in ten seconds), which the app gives the login in its own header, never from `X-Forwarded-For`. A site never imports `better-auth`: its pages only post to these routes.
61
+ The session is one cookie, `better-auth.session_token` (`__Secure-better-auth.session_token`, and `Secure`, when the site is served over https): HttpOnly, SameSite=Lax, for the site's host only (no `Domain`), for seven days from sign-in. A request for a host that is not the site's is answered 421. Sign-in and sign-up are rate limited per visitor address (three tries in ten seconds), which the app gives the login in its own header, never from `X-Forwarded-For`; the counts are rows in the schema's `rateLimit` table, so the limit holds across processes and requests. A site never imports `better-auth`: its pages only post to these routes.
62
62
 
63
63
  ### An email code
64
64
 
@@ -100,16 +100,16 @@ Each `callbackURL` (and `errorCallbackURL`, where an error goes as `?error=<code
100
100
 
101
101
  ## The staff sign-in
102
102
 
103
- `betterAuthStaffLogin` is the app's sign-in for the company's own people on a host, which the `sanoma` bin uses off this machine (`staffLogin(betterAuthStaffLogin, { github })` from `@sanoma-ai/app`). Unlike the visitor login, nobody signs up: a code is sent, and an account made, only for an email the people file declares (anyone else gets the same answer, and nothing is stored or sent), or a GitHub login it declares. Its tables are in the schema `better_auth_staff`, migrated under the advisory lock `sanoma.login.better-auth.better_auth_staff`; its cookie is `sanoma.session_token`, for seven days, on whatever host the app answers on. Its ways in, under `/auth/better-auth/`:
103
+ `betterAuthStaffLogin` is the app's sign-in for the company's own people on a host, which the `sanoma` bin uses off this machine (`staffLogin(betterAuthStaffLogin, { github })` from `@sanoma-ai/app`). Unlike the visitor login, nobody signs up: a code is sent, and an account made, only for an email the people file declares (anyone else gets the same answer, and nothing is stored or sent), or a GitHub login it declares. Its tables are in the schema `better_auth_staff`, which the worker makes beside `better_auth` ([the schema](#the-schema)); its cookie is `sanoma.session_token`, for seven days, on whatever host the app answers on. Its ways in, under `/auth/better-auth/`:
104
104
 
105
105
  - an email code (`/email-otp/send-verification-otp`, `/sign-in/email-otp`), with a sender;
106
- - GitHub (`/sign-in/social`, back at `/callback/github`), with the company's GitHub App's client, matched by the verified email GitHub gives, else the login;
106
+ - GitHub (`/sign-in/social`, back at `/callback/github`), with the company's GitHub App's client, matched by the verified email GitHub gives, else the login (GitHub's `/user` for the sign-in's own token, which is then dropped; the App needs the account permission "Email addresses: read", or a private email ends in Better Auth's `?error=email_not_found`); a GitHub account the files declare by neither is refused as `?error=not_declared` (or, when its unverified email is a row's, Better Auth's `account_not_linked`), with no row left, as is a new GitHub account under a login a row already carries, since GitHub frees a renamed login; declare people by email where you can;
107
107
  - an invite (`/magic-link/verify`), a one-use magic link valid 24 hours that the app mints server-side (`invite`), a new one replacing the person's last; Better Auth's own `/sign-in/magic-link` is not served.
108
108
 
109
- Every other Better Auth route is a 404, a POST whose `Origin` is not the request's own host is a 403, and a `callbackURL`, `errorCallbackURL` or `newUserCallbackURL` that is not a path on the request's host is a 400. Invites are minted one at a time. Each sign-in is told to the app (`signin.recorded` in the ledger, with how).
109
+ Every other Better Auth route is a 404, a POST whose `Origin` is not the request's own host is a 403, and a `callbackURL`, `errorCallbackURL` or `newUserCallbackURL` that is not a path on the request's host is a 400. Invites for one email are minted one at a time, whichever process asks (a lock on the database). Each sign-in is told to the app (`signin.recorded` in the ledger, with how).
110
110
 
111
111
  ### Later
112
112
 
113
- Password reset for visitors, more social providers (Google), and the rate limit kept in the database for several processes.
113
+ Password reset for visitors, and more social providers (Google).
114
114
 
115
115
  The logo the app shows is Better Auth's mark, from the [Better Auth repository](https://github.com/better-auth/better-auth) (`demo/nextjs/components/logo.tsx`), in black or white. It is there only to identify the service.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { prepareBetterAuth, prepareBetterAuthStaff } from "./instance.ts";
1
2
  export { betterAuthLogin } from "./login.ts";
2
3
  export { betterAuthStaffLogin } from "./staff.ts";
3
4
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { withSetup } from "@sanoma-ai/workflows";
2
2
  import { betterAuthOps } from "./connector.js";
3
+ import { prepareBetterAuth } from "./instance.js";
3
4
  import { betterAuthLogin } from "./login.js";
5
+ export { prepareBetterAuth, prepareBetterAuthStaff } from "./instance.js";
4
6
  export { betterAuthLogin } from "./login.js";
5
7
  export { betterAuthStaffLogin } from "./staff.js";
6
8
  /**
@@ -10,5 +12,5 @@ export { betterAuthStaffLogin } from "./staff.js";
10
12
  * signed with the key the app derives for it. The staff login is `betterAuthStaffLogin`, which the
11
13
  * `sanoma` launcher runs on a host.
12
14
  */
13
- export const betterAuth = withSetup(betterAuthOps, { login: betterAuthLogin });
15
+ export const betterAuth = withSetup(betterAuthOps, { prepare: prepareBetterAuth, login: betterAuthLogin });
14
16
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC"}
@@ -2,14 +2,21 @@ import { type BetterAuthOptions } from "better-auth";
2
2
  import { PostgresDialect } from "kysely";
3
3
  import { Pool } from "pg";
4
4
  export type Protocol = "http" | "https";
5
- /** What each login sets on top of what every instance shares. */
6
- export type ExtraOptions = Omit<BetterAuthOptions, "database" | "secret" | "secrets" | "basePath" | "baseURL">;
5
+ /** What each login sets on top of what every instance shares; the rate limit is the tables' (`TABLE_OPTIONS`). */
6
+ export type ExtraOptions = Omit<BetterAuthOptions, "database" | "secret" | "secrets" | "basePath" | "baseURL" | "rateLimit">;
7
+ /** The schema each login's tables are in, on the company's database. */
8
+ export declare const SCHEMAS: {
9
+ readonly visitors: "better_auth";
10
+ readonly staff: "better_auth_staff";
11
+ };
12
+ type Schema = (typeof SCHEMAS)[keyof typeof SCHEMAS];
7
13
  export interface BetterAuthSetup<O extends ExtraOptions> {
8
- databaseUrl: string;
14
+ /** The pool the app's client runs on (`LoginContext.database`): one instance per pool it gives. */
15
+ database(): Promise<Pool>;
9
16
  /** The app's key for this login, 32 bytes as hex (`LoginContext.key`). */
10
17
  key: string;
11
- /** The schema its tables are in: `better_auth` for visitors, `better_auth_staff` for staff. */
12
- schema: string;
18
+ /** The schema its tables are in (`SCHEMAS`). */
19
+ schema: Schema;
13
20
  /** Where its routes are: `/auth/better-auth`. */
14
21
  basePath: string;
15
22
  /** The hosts each protocol's instance answers, as Better Auth's `allowedHosts` takes them. */
@@ -18,77 +25,121 @@ export interface BetterAuthSetup<O extends ExtraOptions> {
18
25
  options: O;
19
26
  }
20
27
  /**
21
- * Opens one Better Auth per protocol on the company's database, after its migrations: `auths` by
22
- * protocol, the `pool` they share, and `close`, which ends it. Throws, the pool ended, when the
23
- * key is not one the app derived or the migrations fail.
28
+ * Better Auth on the company's database, as a function of the request's protocol: the instance
29
+ * for that protocol on the pool `database` gives now, made on its first use (one per pool and
30
+ * protocol, kept as long as the pool is). Throws when the key is not one the app derived; its
31
+ * first use on a pool throws when the schema's tables are still not there, or behind this
32
+ * version's, a minute after a worker starting beside the app should have made them
33
+ * (`prepareBetterAuth`).
24
34
  */
25
- export declare function openBetterAuth<O extends ExtraOptions>({ databaseUrl, key, schema, basePath, hosts, options, }: BetterAuthSetup<O>): Promise<{
26
- auths: Map<Protocol, import("better-auth").Auth<{
27
- rateLimit: {
35
+ export declare function openBetterAuth<O extends ExtraOptions>({ database, key, schema, basePath, hosts, options, }: BetterAuthSetup<O>): (protocol: Protocol) => Promise<import("better-auth").Auth<{
36
+ rateLimit: {
37
+ storage: "database";
38
+ enabled: boolean;
39
+ };
40
+ logger: {
41
+ level: "warn";
42
+ };
43
+ } & O & {
44
+ user: {
45
+ modelName?: "user" | import("better-auth").LiteralString | undefined;
46
+ fields?: Partial<Record<"createdAt" | "email" | "emailVerified" | "image" | "name" | "updatedAt", string>> | undefined;
47
+ validateUserInfo?: (data: {
48
+ user: Partial<import("better-auth").User> & Record<string, unknown>;
49
+ source: import("better-auth").ValidateUserInfoSource;
50
+ }, context: import("better-auth").GenericEndpointContext) => import("better-auth").Awaitable<void | import("better-auth").ValidateUserInfoResult>;
51
+ changeEmail?: {
28
52
  enabled: boolean;
53
+ sendChangeEmailConfirmation?: (data: {
54
+ user: import("better-auth").User;
55
+ newEmail: string;
56
+ url: string;
57
+ token: string;
58
+ }, request?: Request) => Promise<void>;
59
+ updateEmailWithoutVerification?: boolean;
29
60
  };
30
- logger: {
31
- level: "warn";
61
+ deleteUser?: {
62
+ enabled?: boolean;
63
+ sendDeleteAccountVerification?: (data: {
64
+ user: import("better-auth").User;
65
+ url: string;
66
+ token: string;
67
+ }, request?: Request) => Promise<void>;
68
+ beforeDelete?: (user: import("better-auth").User, request?: Request) => Promise<void>;
69
+ afterDelete?: (user: import("better-auth").User, request?: Request) => Promise<void>;
70
+ deleteTokenExpiresIn?: number;
32
71
  };
33
- } & O & {
34
- database: {
35
- dialect: PostgresDialect;
36
- type: "postgres";
37
- schemaName: string;
38
- transaction: boolean;
39
- };
40
- secrets: {
41
- version: number;
42
- value: string;
43
- }[];
44
- secret: string;
45
- basePath: string;
46
- baseURL: {
47
- allowedHosts: string[];
48
- protocol: Protocol;
72
+ additionalFields: {
73
+ github: {
74
+ type: "string";
75
+ required: boolean;
76
+ input: boolean;
77
+ };
49
78
  };
50
- telemetry: {
79
+ };
80
+ database: {
81
+ dialect: PostgresDialect;
82
+ type: "postgres";
83
+ schemaName: Schema;
84
+ transaction: boolean;
85
+ };
86
+ secrets: {
87
+ version: number;
88
+ value: string;
89
+ }[];
90
+ secret: string;
91
+ basePath: string;
92
+ baseURL: {
93
+ allowedHosts: string[];
94
+ protocol: Protocol;
95
+ };
96
+ telemetry: {
97
+ enabled: boolean;
98
+ };
99
+ advanced: {
100
+ disableCSRFCheck?: boolean | undefined;
101
+ crossSubDomainCookies?: {
51
102
  enabled: boolean;
52
- };
53
- advanced: {
54
- disableCSRFCheck?: boolean | undefined;
55
- crossSubDomainCookies?: {
56
- enabled: boolean;
57
- additionalCookies?: string[];
58
- domain?: string;
59
- } | undefined;
60
- cookies?: {
61
- [key: string]: {
62
- name?: string;
63
- attributes?: import("better-auth").CookieOptions;
64
- };
65
- } | undefined;
66
- defaultCookieAttributes?: import("better-auth").CookieOptions | undefined;
67
- cookiePrefix?: string | undefined;
68
- database?: {
69
- defaultFindManyLimit?: number;
70
- generateId?: import("better-auth").GenerateIdFn | false | "serial" | "uuid";
71
- joins?: boolean;
72
- validateSchema?: boolean;
73
- } | undefined;
74
- trustedProxyHeaders?: boolean | undefined;
75
- backgroundTasks?: {
76
- handler: (promise: Promise<unknown>) => void;
103
+ additionalCookies?: string[];
104
+ domain?: string;
105
+ } | undefined;
106
+ cookies?: {
107
+ [key: string]: {
108
+ name?: string;
109
+ attributes?: import("better-auth").CookieOptions;
77
110
  };
78
- skipTrailingSlashes?: boolean;
79
- ipAddress: {
80
- ipAddressHeaders?: string[];
81
- disableIpTracking?: boolean;
82
- ipv6Subnet?: number;
83
- trustedProxies?: string[];
84
- };
85
- useSecureCookies: boolean;
86
- disableOriginCheck: boolean;
111
+ } | undefined;
112
+ defaultCookieAttributes?: import("better-auth").CookieOptions | undefined;
113
+ cookiePrefix?: string | undefined;
114
+ database?: {
115
+ defaultFindManyLimit?: number;
116
+ generateId?: import("better-auth").GenerateIdFn | false | "serial" | "uuid";
117
+ joins?: boolean;
118
+ validateSchema?: boolean;
119
+ } | undefined;
120
+ trustedProxyHeaders?: boolean | undefined;
121
+ backgroundTasks?: {
122
+ handler: (promise: Promise<unknown>) => void;
123
+ };
124
+ skipTrailingSlashes?: boolean;
125
+ ipAddress: {
126
+ ipAddressHeaders?: string[];
127
+ disableIpTracking?: boolean;
128
+ ipv6Subnet?: number;
129
+ trustedProxies?: string[];
87
130
  };
88
- }>>;
89
- pool: Pool;
90
- close: () => Promise<void>;
91
- }>;
131
+ useSecureCookies: boolean;
132
+ disableOriginCheck: boolean;
133
+ };
134
+ }>>;
135
+ /**
136
+ * Makes the visitor login's tables (`better_auth`) on the company's database: the connector's
137
+ * `prepare`, which the worker runs at its start on the database's direct URL. Idempotent: a
138
+ * second run changes nothing.
139
+ */
140
+ export declare const prepareBetterAuth: (databaseUrl: string) => Promise<void>;
141
+ /** Makes the staff login's tables (`better_auth_staff`), as `prepareBetterAuth` the visitors': the launcher's worker runs it, since no config names that login. */
142
+ export declare const prepareBetterAuthStaff: (databaseUrl: string) => Promise<void>;
92
143
  /** A request's protocol: https when it came over TLS or through a proxy that says so (`x-forwarded-proto`). */
93
144
  export declare const protocolOf: (request: Request) => Protocol;
94
145
  /**
@@ -636,14 +687,6 @@ export declare const emailCode: (sendCode?: (email: string, code: string) => Pro
636
687
  };
637
688
  /** Whether each redirect a request names is a path on its own host: `/x`, never `//x` or another origin. */
638
689
  export declare const localRedirects: (url: URL) => boolean;
639
- /** The user's `github` column, the GitHub login: never a person's input, so only the login sets it. */
640
- export declare const GITHUB_FIELD: {
641
- github: {
642
- type: "string";
643
- required: boolean;
644
- input: boolean;
645
- };
646
- };
647
690
  /** A user row, as far as who it vouches for reads it. */
648
691
  export interface User {
649
692
  email: string;
@@ -655,3 +698,4 @@ export declare const identityOf: (user: User) => {
655
698
  email?: string | undefined;
656
699
  github?: string | undefined;
657
700
  };
701
+ export {};
package/dist/instance.js CHANGED
@@ -5,31 +5,60 @@ import { emailOTP } from "better-auth/plugins/email-otp";
5
5
  import { PostgresDialect } from "kysely";
6
6
  import { setTimeout as delay } from "node:timers/promises";
7
7
  import { Client, Pool } from "pg";
8
- /** Each database and schema's migration, once per process however many logins run on it. */
9
- const migrated = new Map();
8
+ /** The schema each login's tables are in, on the company's database. */
9
+ export const SCHEMAS = { visitors: "better_auth", staff: "better_auth_staff" };
10
+ /**
11
+ * What shapes the tables, the same for both logins and laid over theirs: the user's `github`
12
+ * column (the GitHub login: never a person's input, so only the login sets it), and the rate
13
+ * limit's table (`rateLimit.storage: "database"`, so a limit holds across processes and
14
+ * requests). The plugins they run (the email code, magic links) keep no tables of their own.
15
+ */
16
+ const TABLE_OPTIONS = {
17
+ user: { additionalFields: { github: { type: "string", required: false, input: false } } },
18
+ rateLimit: { storage: "database" },
19
+ };
20
+ /** Better Auth's database options for `schema`, on `pool`: the migrations' and the instances' the same. */
21
+ const databaseOn = (pool, schema) => ({
22
+ dialect: new PostgresDialect({ pool }),
23
+ type: "postgres",
24
+ // Schema-qualified queries, not a search_path, which the URL's own `options` could undo.
25
+ schemaName: schema,
26
+ transaction: true,
27
+ });
10
28
  /**
11
- * Opens one Better Auth per protocol on the company's database, after its migrations: `auths` by
12
- * protocol, the `pool` they share, and `close`, which ends it. Throws, the pool ended, when the
13
- * key is not one the app derived or the migrations fail.
29
+ * Each database and schema found prepared, for the process's life: a pool that finds its schema
30
+ * prepared spares every pool on that database the check after it. A check in flight is its own
31
+ * pool's and no one else's (on a Worker a request's pool goes with the request), so nothing waits
32
+ * on another request's work, and a failed check leaves nothing behind.
14
33
  */
15
- export async function openBetterAuth({ databaseUrl, key, schema, basePath, hosts, options, }) {
34
+ const prepared = new Set();
35
+ /** What a pool connects to, as the key a check is remembered by: its connection string, else its host and database. */
36
+ function databaseOf(pool) {
37
+ const { connectionString, host, port, database } = pool.options;
38
+ return connectionString ?? `${host}:${port}/${database}`;
39
+ }
40
+ /** How long an opening login waits for a worker starting beside it to prepare the schema, and how often it looks. */
41
+ const PREPARED_WAIT_MS = 60_000;
42
+ const PREPARED_POLL_MS = 1_000;
43
+ /**
44
+ * Better Auth on the company's database, as a function of the request's protocol: the instance
45
+ * for that protocol on the pool `database` gives now, made on its first use (one per pool and
46
+ * protocol, kept as long as the pool is). Throws when the key is not one the app derived; its
47
+ * first use on a pool throws when the schema's tables are still not there, or behind this
48
+ * version's, a minute after a worker starting beside the app should have made them
49
+ * (`prepareBetterAuth`).
50
+ */
51
+ export function openBetterAuth({ database, key, schema, basePath, hosts, options, }) {
16
52
  // Never Better Auth's own fallback (an environment variable, or its default secret).
17
53
  if (!/^[0-9a-f]{64}$/.test(key))
18
54
  throw new Error("better-auth: the app gives no key to sign cookies with");
19
- const pool = new Pool({ connectionString: databaseUrl });
20
- pool.on("error", (err) => console.warn(`better-auth: a database connection failed: ${err.message}`));
21
- const optionsFor = (protocol) => ({
22
- // On only in production by default; in memory, per process.
23
- rateLimit: { enabled: true },
55
+ const optionsFor = (pool, protocol) => ({
56
+ // On only in production by default; its counts in the schema's table, never in memory.
57
+ rateLimit: { enabled: true, ...TABLE_OPTIONS.rateLimit },
24
58
  logger: { level: "warn" },
25
59
  ...options,
26
- // Schema-qualified queries, not a search_path, which the URL's own `options` could undo.
27
- database: {
28
- dialect: new PostgresDialect({ pool }),
29
- type: "postgres",
30
- schemaName: schema,
31
- transaction: true,
32
- },
60
+ user: { ...options.user, ...TABLE_OPTIONS.user },
61
+ database: databaseOn(pool, schema),
33
62
  // `secrets` over `secret`, else BETTER_AUTH_SECRETS in the environment would win.
34
63
  secrets: [{ version: 1, value: key }],
35
64
  secret: key,
@@ -38,7 +67,8 @@ export async function openBetterAuth({ databaseUrl, key, schema, basePath, hosts
38
67
  baseURL: { allowedHosts: [...(hosts.get(protocol) ?? [])], protocol },
39
68
  telemetry: { enabled: false },
40
69
  advanced: {
41
- // The app sets it from the socket; Better Auth would otherwise believe a client's X-Forwarded-For.
70
+ // The app sets it from the socket, or from its host's own header; Better Auth would
71
+ // otherwise believe a client's X-Forwarded-For.
42
72
  ipAddress: { ipAddressHeaders: [CLIENT_IP_HEADER] },
43
73
  useSecureCookies: protocol === "https",
44
74
  // Its default under NODE_ENV=test is off: the same check everywhere.
@@ -46,48 +76,83 @@ export async function openBetterAuth({ databaseUrl, key, schema, basePath, hosts
46
76
  ...options.advanced,
47
77
  },
48
78
  });
49
- try {
50
- const protocols = [...hosts.keys()];
51
- await migrateOnce(databaseUrl, schema, optionsFor(protocols[0]));
52
- // After the migrations: its start checks the tables, and says the schema is out of date otherwise.
53
- const auths = new Map(protocols.map((protocol) => [protocol, betterAuth(optionsFor(protocol))]));
54
- return { auths, pool, close: () => pool.end() };
55
- }
56
- catch (err) {
57
- await pool.end();
58
- throw err;
79
+ /** Each pool's instances, by protocol, for as long as the pool is referenced. */
80
+ const made = new WeakMap();
81
+ return async (protocol) => {
82
+ const pool = await database();
83
+ const found = `${databaseOf(pool)}#${schema}`;
84
+ if (!prepared.has(found)) {
85
+ await checkPrepared(pool, schema);
86
+ prepared.add(found);
87
+ }
88
+ let auths = made.get(pool);
89
+ if (!auths)
90
+ made.set(pool, (auths = new Map()));
91
+ let auth = auths.get(protocol);
92
+ if (!auth)
93
+ auths.set(protocol, (auth = betterAuth(optionsFor(pool, protocol))));
94
+ return auth;
95
+ };
96
+ }
97
+ /**
98
+ * Waits for `schema` to hold every table and column this version's migrations would make (Better
99
+ * Auth's own migration plan, read and never run), looking every second for a minute, since a
100
+ * deployment's worker starts beside its app; then refuses: the worker makes them, and the app
101
+ * never does.
102
+ */
103
+ async function checkPrepared(pool, schema) {
104
+ const database = databaseOn(pool, schema);
105
+ for (const deadline = Date.now() + PREPARED_WAIT_MS;; await delay(PREPARED_POLL_MS)) {
106
+ const { toBeCreated, toBeAdded } = await getMigrations({ ...TABLE_OPTIONS, database });
107
+ if (toBeCreated.length === 0 && toBeAdded.length === 0)
108
+ return;
109
+ if (Date.now() >= deadline)
110
+ throw new Error(`better-auth: the database's ${schema} tables are missing or behind this version's: the worker makes them at its start, and the app never does`);
59
111
  }
60
112
  }
61
- /** Better Auth's migrations, once per database and schema in this process, under the advisory lock across processes. */
62
- function migrateOnce(databaseUrl, schema, options) {
63
- const at = `${databaseUrl}#${schema}`;
64
- let done = migrated.get(at);
113
+ /** `run` once per `key` in this process, again after it fails. */
114
+ function once(memo, key, run) {
115
+ let done = memo.get(key);
65
116
  if (!done) {
66
- done = migrate(databaseUrl, schema, options).catch((err) => {
67
- migrated.delete(at);
117
+ done = run().catch((err) => {
118
+ memo.delete(key);
68
119
  throw err;
69
120
  });
70
- migrated.set(at, done);
121
+ memo.set(key, done);
71
122
  }
72
123
  return done;
73
124
  }
125
+ /** Each database and schema's migration, once per process however many workers run on it. */
126
+ const migrated = new Map();
127
+ /** The preparation of one login's `schema`: its migrations, once per process, under an advisory lock across processes. */
128
+ const preparing = (schema) => (databaseUrl) => once(migrated, `${databaseUrl}#${schema}`, () => migrate(databaseUrl, schema));
129
+ /**
130
+ * Makes the visitor login's tables (`better_auth`) on the company's database: the connector's
131
+ * `prepare`, which the worker runs at its start on the database's direct URL. Idempotent: a
132
+ * second run changes nothing.
133
+ */
134
+ export const prepareBetterAuth = preparing(SCHEMAS.visitors);
135
+ /** Makes the staff login's tables (`better_auth_staff`), as `prepareBetterAuth` the visitors': the launcher's worker runs it, since no config names that login. */
136
+ export const prepareBetterAuthStaff = preparing(SCHEMAS.staff);
74
137
  /**
75
138
  * Holds the lock on a session of its own until that ends, whatever fails; polled, never waited
76
- * for by a blocked statement, as the runtime's own preparation does. The migrations make the schema.
139
+ * for by a blocked statement, as the runtime's own preparation does. The migrations make the
140
+ * schema, on a pool of their own, ended after.
77
141
  */
78
- async function migrate(databaseUrl, schema, options) {
142
+ async function migrate(databaseUrl, schema) {
79
143
  const conn = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 10_000 });
80
144
  // An error nobody listens for ends the process; the query in flight fails with it.
81
145
  conn.on("error", (err) => console.warn(`better-auth: the migration's connection failed: ${err.message}`));
82
146
  await conn.connect();
147
+ const pool = new Pool({ connectionString: databaseUrl, max: 1 });
83
148
  try {
84
149
  const ask = "SELECT pg_try_advisory_lock(hashtext($1)) AS locked";
85
150
  while (!(await conn.query(ask, [`sanoma.login.better-auth.${schema}`])).rows[0].locked)
86
151
  await delay(50);
87
- await (await getMigrations(options)).runMigrations();
152
+ await (await getMigrations({ ...TABLE_OPTIONS, database: databaseOn(pool, schema) })).runMigrations();
88
153
  }
89
154
  finally {
90
- await conn.end().catch(() => undefined);
155
+ await Promise.allSettled([conn.end(), pool.end()]);
91
156
  }
92
157
  }
93
158
  /** A request's protocol: https when it came over TLS or through a proxy that says so (`x-forwarded-proto`). */
@@ -126,8 +191,6 @@ export const localRedirects = (url) => REDIRECTS.every((name) => {
126
191
  const to = url.searchParams.get(name);
127
192
  return to === null || (to.startsWith("/") && !to.startsWith("//") && !to.startsWith("/\\"));
128
193
  });
129
- /** The user's `github` column, the GitHub login: never a person's input, so only the login sets it. */
130
- export const GITHUB_FIELD = { github: { type: "string", required: false, input: false } };
131
194
  /** What a user row vouches for: the email when verified, the GitHub login when GitHub said it. */
132
195
  export const identityOf = (user) => ({
133
196
  ...(user.emailVerified && { email: user.email }),
@@ -1 +1 @@
1
- {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
package/dist/login.d.ts CHANGED
@@ -5,9 +5,9 @@ import type { LoginContext, LoginHalf } from "@sanoma-ai/workflows";
5
5
  * (`context.social.github`), a visitor also signs up and in with GitHub (`/sign-in/social`, which
6
6
  * joins the account of GitHub's verified email), or links it once signed in (`/link-social`), and
7
7
  * is then said to be that GitHub login (`verify`'s `github`). Its tables are in schema
8
- * `better_auth` on the company's database, migrated here before the first site starts, its
9
- * cookies signed with the app's key for it (`context.key`). The site's origins are its allowed
10
- * hosts (a request for another host is refused) and set the cookie's `Secure` flag: an `https`
11
- * origin's is.
8
+ * `better_auth` on the company's database, which the worker makes (`prepareBetterAuth`), reached
9
+ * through the pool the app gives each request (`context.database`), its cookies signed with the
10
+ * app's key for it (`context.key`). The site's origins are its allowed hosts (a request for
11
+ * another host is refused) and set the cookie's `Secure` flag: an `https` origin's is.
12
12
  */
13
- export declare function betterAuthLogin({ databaseUrl, key, origins, sendCode, social, }: LoginContext): Promise<LoginHalf>;
13
+ export declare function betterAuthLogin({ database, key, origins, sendCode, social }: LoginContext): LoginHalf;
package/dist/login.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getHostFromSource, matchesHostPattern } from "better-auth";
2
- import { emailCode, GITHUB_FIELD, identityOf, localRedirects, openBetterAuth, withSignOutBody, } from "./instance.js";
3
- /** The schema Better Auth's tables (`user`, `session`, `account`, `verification`) live in, on the company's database. */
4
- const SCHEMA = "better_auth";
2
+ import { emailCode, identityOf, localRedirects, openBetterAuth, SCHEMAS, withSignOutBody, } from "./instance.js";
3
+ /** The schema Better Auth's tables (`user`, `session`, `account`, `verification`, `rateLimit`) live in, on the company's database. */
4
+ const SCHEMA = SCHEMAS.visitors;
5
5
  /** Where its routes are: a site's pages POST to `<BASE_PATH>/sign-up/email`, `/sign-in/email`, `/sign-in/email-otp`… */
6
6
  const BASE_PATH = "/auth/better-auth";
7
7
  const SIGN_OUT = `${BASE_PATH}/sign-out`;
@@ -11,19 +11,19 @@ const SIGN_OUT = `${BASE_PATH}/sign-out`;
11
11
  * (`context.social.github`), a visitor also signs up and in with GitHub (`/sign-in/social`, which
12
12
  * joins the account of GitHub's verified email), or links it once signed in (`/link-social`), and
13
13
  * is then said to be that GitHub login (`verify`'s `github`). Its tables are in schema
14
- * `better_auth` on the company's database, migrated here before the first site starts, its
15
- * cookies signed with the app's key for it (`context.key`). The site's origins are its allowed
16
- * hosts (a request for another host is refused) and set the cookie's `Secure` flag: an `https`
17
- * origin's is.
14
+ * `better_auth` on the company's database, which the worker makes (`prepareBetterAuth`), reached
15
+ * through the pool the app gives each request (`context.database`), its cookies signed with the
16
+ * app's key for it (`context.key`). The site's origins are its allowed hosts (a request for
17
+ * another host is refused) and set the cookie's `Secure` flag: an `https` origin's is.
18
18
  */
19
- export async function betterAuthLogin({ databaseUrl, key, origins, sendCode, social, }) {
19
+ export function betterAuthLogin({ database, key, origins, sendCode, social }) {
20
20
  const github = social?.github;
21
21
  const sites = sitesOf(origins);
22
22
  const hosts = new Map();
23
23
  for (const { protocol, host } of sites)
24
24
  hosts.set(protocol, [...(hosts.get(protocol) ?? []), host]);
25
- const { auths, close } = await openBetterAuth({
26
- databaseUrl,
25
+ const auth = openBetterAuth({
26
+ database,
27
27
  key,
28
28
  schema: SCHEMA,
29
29
  basePath: BASE_PATH,
@@ -31,7 +31,6 @@ export async function betterAuthLogin({ databaseUrl, key, origins, sendCode, soc
31
31
  options: {
32
32
  emailAndPassword: { enabled: true },
33
33
  plugins: sendCode ? [emailCode(sendCode)] : [],
34
- user: { additionalFields: GITHUB_FIELD },
35
34
  ...(github && {
36
35
  socialProviders: { github: { clientId: github.clientId, clientSecret: github.clientSecret } },
37
36
  // GitHub stays untrusted, and the account's own email must be verified (Better Auth's
@@ -52,7 +51,7 @@ export async function betterAuthLogin({ databaseUrl, key, origins, sendCode, soc
52
51
  const authFor = (request) => {
53
52
  const host = getHostFromSource(request, false) ?? "";
54
53
  const site = sites.find((s) => matchesHostPattern(host, s.host));
55
- return site && auths.get(site.protocol);
54
+ return site && auth(site.protocol);
56
55
  };
57
56
  const methods = [
58
57
  "password",
@@ -61,11 +60,11 @@ export async function betterAuthLogin({ databaseUrl, key, origins, sendCode, soc
61
60
  ];
62
61
  return {
63
62
  async verify(request) {
64
- const auth = authFor(request);
65
- if (!auth)
63
+ const own = authFor(request);
64
+ if (!own)
66
65
  throw new Error(`better-auth: ${request.headers.get("host")} is not one of the site's hosts`);
67
66
  // Null for a missing, bad or signed-out cookie.
68
- const session = await auth.api.getSession({ headers: request.headers });
67
+ const session = await (await own).api.getSession({ headers: request.headers });
69
68
  if (!session)
70
69
  return null;
71
70
  return { subject: session.user.id, ...identityOf(session.user) };
@@ -74,18 +73,17 @@ export async function betterAuthLogin({ databaseUrl, key, origins, sendCode, soc
74
73
  const { pathname } = new URL(request.url);
75
74
  if (pathname !== BASE_PATH && !pathname.startsWith(`${BASE_PATH}/`))
76
75
  return undefined;
77
- // Better Auth throws for a host it does not allow, which would be a 500.
78
- const auth = authFor(request);
79
- if (!auth)
80
- return new Response("Misdirected Request", { status: 421 });
81
76
  // Better Auth checks a redirect in a body against the site's hosts; one in the query must be a path here.
82
77
  if (!localRedirects(new URL(request.url)))
83
78
  return new Response("Bad Request", { status: 400 });
84
- return auth.handler(withSignOutBody(request, SIGN_OUT));
79
+ // Better Auth throws for a host it does not allow, which would be a 500.
80
+ const own = authFor(request);
81
+ if (!own)
82
+ return new Response("Misdirected Request", { status: 421 });
83
+ return (await own).handler(withSignOutBody(request, SIGN_OUT));
85
84
  },
86
85
  signOut: SIGN_OUT,
87
86
  methods,
88
- close,
89
87
  };
90
88
  }
91
89
  /** A visitor has one GitHub account at most: another is refused (Better Auth's `unable_to_link_account`). */
package/dist/login.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
package/dist/staff.d.ts CHANGED
@@ -6,6 +6,7 @@ import { type LoginContext, type LoginHalf } from "@sanoma-ai/workflows";
6
6
  * cookie, `sanoma.session_token`, for the host, seven days. A code is sent, and a user made, only
7
7
  * for a person `staff.admits`: an undeclared email gets the same answer and leaves nothing.
8
8
  * Each sign-in is told to `staff.record`. It answers on any host the app does, so a change
9
- * (any POST) to its routes must come from the page's own origin.
9
+ * (any POST) to its routes must come from the page's own origin. It runs on the pool the app
10
+ * gives each request (`database`), and keeps nothing between requests.
10
11
  */
11
- export declare function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }: LoginContext): Promise<LoginHalf>;
12
+ export declare function betterAuthStaffLogin({ database, key, sendCode, staff }: LoginContext): LoginHalf;
package/dist/staff.js CHANGED
@@ -2,14 +2,16 @@ import { INVITE_SECONDS, sameOrigin, } from "@sanoma-ai/workflows";
2
2
  import { createAuthMiddleware } from "better-auth/api";
3
3
  import { magicLink } from "better-auth/plugins/magic-link";
4
4
  import { createHash } from "node:crypto";
5
- import { emailCode, GITHUB_FIELD, identityOf, localRedirects, openBetterAuth, protocolOf, withSignOutBody, } from "./instance.js";
5
+ import { emailCode, identityOf, localRedirects, openBetterAuth, protocolOf, SCHEMAS, withSignOutBody, } from "./instance.js";
6
+ /** Says nothing: a listener for a checked-out client's `error`, whose query fails on its own. */
7
+ const quiet = () => undefined;
6
8
  // The staff login: Better Auth again, as a second instance in a schema of its own, signing in only
7
9
  // the people the files declare. An email code to a declared email, GitHub through the company's
8
10
  // own client, and invites (one-use magic links the app mints for a declared email); no password.
9
11
  // A code or a user row is never made for anyone else: the declared-email check is in Better Auth's
10
12
  // hooks, not its `disableSignUp`, which would send no code until a user row exists.
11
13
  /** The schema the staff login's tables are in, beside the visitors' `better_auth`. */
12
- const SCHEMA = "better_auth_staff";
14
+ const SCHEMA = SCHEMAS.staff;
13
15
  const BASE_PATH = "/auth/better-auth";
14
16
  const SIGN_OUT = `${BASE_PATH}/sign-out`;
15
17
  const SEND_CODE = "/email-otp/send-verification-otp";
@@ -27,6 +29,13 @@ const ROUTES = new Set([
27
29
  ]);
28
30
  /** The routes of the email code, which answer only when the app can send one. */
29
31
  const CODE_ROUTES = new Set([SEND_CODE, "/sign-in/email-otp"]);
32
+ /** GitHub's tokens are never kept: a GitHub App's user token reaches what the App and the person both reach. */
33
+ const dropTokens = async () => ({ data: { accessToken: null, refreshToken: null, idToken: null } });
34
+ /** A GitHub account gone (a code proves an email its unverified row had, and drops its accounts): its user is no longer that login. */
35
+ const forgetLogin = async (account, ctx) => {
36
+ if (account?.providerId === "github" && ctx)
37
+ await ctx.context.internalAdapter.updateUser(account.userId, { github: null });
38
+ };
30
39
  /** How a session was made, by the route that made it. */
31
40
  const METHOD_OF = {
32
41
  "/sign-in/email-otp": "code",
@@ -40,16 +49,25 @@ const METHOD_OF = {
40
49
  * cookie, `sanoma.session_token`, for the host, seven days. A code is sent, and a user made, only
41
50
  * for a person `staff.admits`: an undeclared email gets the same answer and leaves nothing.
42
51
  * Each sign-in is told to `staff.record`. It answers on any host the app does, so a change
43
- * (any POST) to its routes must come from the page's own origin.
52
+ * (any POST) to its routes must come from the page's own origin. It runs on the pool the app
53
+ * gives each request (`database`), and keeps nothing between requests.
44
54
  */
45
- export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }) {
55
+ export function betterAuthStaffLogin({ database, key, sendCode, staff }) {
46
56
  if (!staff)
47
57
  throw new Error("better-auth: the staff login needs the app's staff context");
48
58
  const github = staff.social.github;
49
59
  /** Each invite's token as Better Auth hands it over, by email, until `invite` takes it. */
50
60
  const minted = new Map();
51
- const { auths, close } = await openBetterAuth({
52
- databaseUrl,
61
+ /** The login GitHub's profile names, by the request it came in, until `keepLogin` writes it. */
62
+ const logins = new WeakMap();
63
+ /** Keeps the login GitHub named for this request on the account's user, once the account is made or refreshed. */
64
+ const keepLogin = async (account, ctx) => {
65
+ const login = account?.providerId === "github" && ctx ? logins.get(ctx) : undefined;
66
+ if (login)
67
+ await ctx.context.internalAdapter.updateUser(account.userId, { github: login });
68
+ };
69
+ const auth = openBetterAuth({
70
+ database,
53
71
  key,
54
72
  schema: SCHEMA,
55
73
  basePath: BASE_PATH,
@@ -60,14 +78,44 @@ export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }
60
78
  ]),
61
79
  options: {
62
80
  session: { expiresIn: SESSION_SECONDS },
63
- user: { additionalFields: GITHUB_FIELD },
81
+ user: {
82
+ // Nobody the files do not declare gets a row, whatever the route: by the verified email
83
+ // the route proved, or by the login GitHub's own profile names (Better Auth sets no field
84
+ // from a profile that a person may not set, so the login is kept by `keepLogin`), before
85
+ // anything is written. A GitHub sign-in is checked again each time, with the fresh profile.
86
+ validateUserInfo: async ({ user, source }, ctx) => {
87
+ const refused = {
88
+ error: "not_declared",
89
+ errorDescription: "This company's files declare nobody with that account",
90
+ };
91
+ const said = source.oauth?.providerId === "github" ? source.oauth.profile?.login : undefined;
92
+ const login = typeof said === "string" ? said : undefined;
93
+ if (login)
94
+ logins.set(ctx, login);
95
+ const { adapter, internalAdapter } = ctx.context;
96
+ // A new GitHub account under a login a row already carries is another account (GitHub
97
+ // frees a renamed login): it is not the declared person, whatever the files say.
98
+ if (login && source.action === "create-user") {
99
+ const holder = await adapter.findOne({ model: "user", where: [{ field: "github", value: login }] });
100
+ if (holder)
101
+ return refused;
102
+ }
103
+ // Signing in again, or linking: GitHub's email may have moved; the row's verified one still vouches.
104
+ const row = typeof user.id === "string" ? await internalAdapter.findUserById(user.id) : null;
105
+ const identity = {
106
+ ...identityOf(user),
107
+ ...(row && row.emailVerified && { email: row.email }),
108
+ ...(login && { github: login }),
109
+ };
110
+ return staff.admits(identity) ? undefined : refused;
111
+ },
112
+ },
64
113
  advanced: { cookiePrefix: "sanoma" },
65
114
  ...(github && {
66
115
  socialProviders: {
67
116
  github: {
68
117
  clientId: github.clientId,
69
118
  clientSecret: github.clientSecret,
70
- mapProfileToUser: (profile) => ({ github: profile.login }),
71
119
  },
72
120
  },
73
121
  }),
@@ -91,11 +139,10 @@ export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }
91
139
  }),
92
140
  },
93
141
  databaseHooks: {
94
- user: {
95
- create: {
96
- // Nobody the files do not declare gets a row, whatever the route.
97
- before: async (user) => (staff.admits(identityOf(user)) ? undefined : false),
98
- },
142
+ account: {
143
+ create: { before: dropTokens, after: keepLogin },
144
+ update: { before: dropTokens, after: keepLogin },
145
+ delete: { after: forgetLogin },
99
146
  },
100
147
  session: {
101
148
  create: {
@@ -112,34 +159,63 @@ export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }
112
159
  },
113
160
  },
114
161
  });
115
- const authFor = (request) => auths.get(protocolOf(request));
162
+ const authFor = (request) => auth(protocolOf(request));
116
163
  const methods = [
117
164
  ...(sendCode ? ["code"] : []),
118
165
  ...(github ? ["github"] : []),
119
166
  "invite",
120
167
  ];
121
- /** Mints `email`'s invite, server-side, its token from `minted`; a new one replaces the last. */
122
- async function mint(email, request) {
123
- const auth = authFor(request);
168
+ /** The mints of this process, one after another. */
169
+ let mints = Promise.resolve();
170
+ /**
171
+ * Mints `email`'s invite, server-side, its token from `minted`; a new one replaces the last. One
172
+ * at a time per email, whichever process asks: a transaction of its own holds Postgres's lock
173
+ * for the email while the old row goes and the new one is written on the pool's other
174
+ * connections (a transaction's lock is one connection's for the transaction, safe through a
175
+ * pooler), so two mints at once leave one invite, and `minted` hands over the right token. One
176
+ * at a time in the process as well (`mints`): the lock's connection is one of the pool's, and
177
+ * many held at once, each waiting for another of the pool's, would leave none for Better Auth.
178
+ */
179
+ function mint(email, request) {
180
+ const minting = mints.then(() => mintNow(email, request));
181
+ mints = minting.catch(() => undefined);
182
+ return minting;
183
+ }
184
+ async function mintNow(email, request) {
185
+ const own = await authFor(request);
124
186
  const normalized = email.toLowerCase();
125
- // A new invite replaces the last: the old one's row goes.
126
- const { adapter } = await auth.$context;
127
- await adapter.deleteMany({
128
- model: "verification",
129
- where: [{ field: "value", value: JSON.stringify({ type: "magic-link", email: normalized }) }],
130
- });
131
- await auth.api.signInMagicLink({ body: { email: normalized, callbackURL: "/" }, headers: request.headers });
187
+ const lock = await (await database()).connect();
188
+ // A checked-out client with no listener would throw its connection's error as uncaught; the query fails instead.
189
+ lock.on("error", quiet);
190
+ try {
191
+ await lock.query("BEGIN");
192
+ await lock.query("SELECT pg_advisory_xact_lock(hashtext($1))", [`sanoma.login.invite.${normalized}`]);
193
+ // A new invite replaces the last: the old one's row goes.
194
+ const { adapter } = await own.$context;
195
+ await adapter.deleteMany({
196
+ model: "verification",
197
+ where: [{ field: "value", value: JSON.stringify({ type: "magic-link", email: normalized }) }],
198
+ });
199
+ await own.api.signInMagicLink({ body: { email: normalized, callbackURL: "/" }, headers: request.headers });
200
+ await lock.query("COMMIT");
201
+ }
202
+ catch (err) {
203
+ await lock.query("ROLLBACK").catch(() => undefined);
204
+ throw err;
205
+ }
206
+ finally {
207
+ lock.off("error", quiet);
208
+ lock.release();
209
+ }
132
210
  const token = minted.get(normalized);
133
211
  minted.delete(normalized);
134
212
  if (!token)
135
213
  throw new Error("better-auth: the invite was not minted");
136
214
  return token;
137
215
  }
138
- /** The invite being minted, which the next waits for. */
139
- let invites = Promise.resolve();
140
216
  return {
141
217
  async verify(request) {
142
- const session = await authFor(request).api.getSession({ headers: request.headers });
218
+ const session = await (await authFor(request)).api.getSession({ headers: request.headers });
143
219
  return session ? { subject: session.user.id, ...identityOf(session.user) } : null;
144
220
  },
145
221
  async handle(request) {
@@ -154,20 +230,14 @@ export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }
154
230
  // Any host is allowed, so Better Auth trusts any redirect: only a path on this host is taken.
155
231
  if (!localRedirects(new URL(request.url)))
156
232
  return new Response("Bad Request", { status: 400 });
157
- return authFor(request).handler(withSignOutBody(request, SIGN_OUT));
233
+ return (await authFor(request)).handler(withSignOutBody(request, SIGN_OUT));
158
234
  },
159
235
  signOut: SIGN_OUT,
160
236
  methods,
161
- invite(email, request) {
162
- // One at a time: each takes its token from `minted` and replaces the last invite's row.
163
- const next = invites.then(() => mint(email, request));
164
- invites = next.catch(() => undefined);
165
- return next;
166
- },
237
+ invite: mint,
167
238
  async invitation(token) {
168
- // The first of the instances: they share the tables.
169
- const auth = auths.values().next().value;
170
- const { internalAdapter } = await auth.$context;
239
+ // Either protocol's instance: they share the tables.
240
+ const { internalAdapter } = await (await auth("https")).$context;
171
241
  const found = await internalAdapter.findVerificationValue(`magic-link:${hashed(token)}`);
172
242
  if (!found || found.expiresAt < new Date())
173
243
  return undefined;
@@ -177,7 +247,6 @@ export async function betterAuthStaffLogin({ databaseUrl, key, sendCode, staff }
177
247
  const query = new URLSearchParams({ token, callbackURL: "/" });
178
248
  return { email, path: `${BASE_PATH}/magic-link/verify?${query}` };
179
249
  },
180
- close,
181
250
  };
182
251
  }
183
252
  /** A token as the magic link plugin stores it (`storeToken: "hashed"`): SHA-256, base64url without padding. */
package/dist/staff.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"staff.js","sourceRoot":"","sources":["../src/staff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"staff.js","sourceRoot":"","sources":["../src/staff.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanoma-ai/connector-better-auth",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Better Auth for Sanoma: the visitor login of public sites (email and password, or an email code) and the staff sign-in on a host (email code, invite links, GitHub), on the company's own database",
5
5
  "keywords": [
6
6
  "auth",
@@ -44,13 +44,12 @@
44
44
  "pg": "^8.23.1"
45
45
  },
46
46
  "devDependencies": {
47
- "@sanoma-ai/app": "0.10.8",
48
47
  "@sanoma-ai/testing": "0.1.1",
49
- "@sanoma-ai/workflows": "0.11.4",
48
+ "@sanoma-ai/workflows": "0.11.13",
50
49
  "@types/pg": "^8.23.1"
51
50
  },
52
51
  "peerDependencies": {
53
- "@sanoma-ai/workflows": "^0.11.4"
52
+ "@sanoma-ai/workflows": "^0.11.13"
54
53
  },
55
54
  "engines": {
56
55
  "node": ">=26"