@procedures/better-auth 0.13.2 → 0.14.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.
package/dist/auth.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { Db, MongoClient } from "mongodb";
2
2
  import type { BetterAuthConfig } from "./config.js";
3
3
  /**
4
- * The parts of a Better Auth server that an app uses. Mount `handler` on `config.basePath`, for
5
- * example with `toNodeHandler` of `better-auth/node`. `SessionPrincipals` reads `api.getSession`.
4
+ * The parts of a Better Auth server that an app uses. Mount `handler` on `config.basePath` with
5
+ * `betterAuthHandler()`, which adds the credentialed CORS of the trusted origins. `SessionPrincipals`
6
+ * reads `api.getSession`.
6
7
  */
7
8
  export interface BetterAuthServer {
8
9
  /** The handler of the auth routes. */
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,QAAQ,CAAC,GAAG,EAAE;QACZ,qEAAqE;QACrE,UAAU,CAAC,OAAO,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;KAChI,CAAC;CACH;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAyBxG"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,QAAQ,CAAC,GAAG,EAAE;QACZ,qEAAqE;QACrE,UAAU,CAAC,OAAO,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;KAChI,CAAC;CACH;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAExG"}
package/dist/auth.js CHANGED
@@ -4,37 +4,13 @@
4
4
  * password. Better Auth does the OAuth state, PKCE and the CSRF checks.
5
5
  *
6
6
  * Give Better Auth its own database: its `user` collection must not mix with the data of the app.
7
- * The app mounts `auth.handler` on `config.basePath`: the routes belong to the app.
7
+ * The app mounts `auth.handler` on `config.basePath` (`betterAuthHandler()`): the routes belong to the app.
8
8
  */
9
9
  import { betterAuth } from "better-auth";
10
10
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
11
- import { organization } from "better-auth/plugins";
12
- import { passkey } from "@better-auth/passkey";
11
+ import { betterAuthOptions } from "./options.js";
13
12
  /** This function makes the Better Auth server of a configuration. */
14
13
  export function createBetterAuth(db, client, config) {
15
- const testHooks = {
16
- user: {
17
- create: {
18
- before: async (user) => ({ data: { ...user, emailVerified: true } }),
19
- },
20
- },
21
- };
22
- return betterAuth({
23
- appName: config.appName,
24
- basePath: config.basePath,
25
- baseURL: config.baseURL,
26
- secret: config.secret,
27
- database: mongodbAdapter(db, { client }),
28
- trustedOrigins: [...config.trustedOrigins],
29
- socialProviders: {
30
- ...(config.github ? { github: { ...config.github } } : {}),
31
- ...(config.google ? { google: { ...config.google } } : {}),
32
- },
33
- emailAndPassword: { enabled: config.testAccounts },
34
- // A test account has no email check: the test server trusts its email
35
- ...(config.testAccounts ? { databaseHooks: testHooks } : {}),
36
- // The passkeys expect the origins of the app, not the Origin header of each request
37
- plugins: [organization(), passkey({ rpID: config.rpID, rpName: config.appName, origin: [...config.trustedOrigins] })],
38
- });
14
+ return betterAuth(betterAuthOptions(config, mongodbAdapter(db, { client })));
39
15
  }
40
16
  //# sourceMappingURL=auth.js.map
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAiB/C,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,EAAM,EAAE,MAAmB,EAAE,MAAwB;IACpF,MAAM,SAAS,GAAG;QAChB,IAAI,EAAE;YACJ,MAAM,EAAE;gBACN,MAAM,EAAE,KAAK,EAAE,IAA6B,EAA8C,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;aAC1I;SACF;KACF,CAAC;IACF,OAAO,UAAU,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;QACxC,cAAc,EAAE,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC;QAC1C,eAAe,EAAE;YACf,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D;QACD,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE;QAClD,sEAAsE;QACtE,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,oFAAoF;QACpF,OAAO,EAAE,CAAC,YAAY,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;KACtH,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAgBjD,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,EAAM,EAAE,MAAmB,EAAE,MAAwB;IACpF,OAAO,UAAU,CAAC,iBAAiB,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC"}
package/dist/config.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * - `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`: the sign-in with Google.
8
8
  * - `AUTH_TEST_ACCOUNTS=1`: accounts with an email and a password, for the tests. Production refuses it.
9
9
  * - `PASSKEY_RP_ID`: the domain of the passkeys (default: the host of the first trusted origin).
10
+ * - `AUTH_CROSS_SITE_COOKIES=1`: the session cookies get `SameSite=None` and `Secure`, for a page on another site.
10
11
  */
11
12
  /** The client id and the secret of an OAuth provider. */
12
13
  export interface OAuthClient {
@@ -29,6 +30,12 @@ export interface BetterAuthConfig {
29
30
  readonly testAccounts: boolean;
30
31
  /** The relying party of the passkeys: a domain of the origins of the app. */
31
32
  readonly rpID: string;
33
+ /**
34
+ * True: the cookies of Better Auth get `SameSite=None` and `Secure`, so a page on another site
35
+ * sends them (CLI-49). A browser keeps a `Secure` cookie only over HTTPS or from `localhost`.
36
+ * The default is false: `SameSite=Lax`.
37
+ */
38
+ readonly crossSiteCookies?: boolean | undefined;
32
39
  }
33
40
  /** The options of `betterAuthConfig`. */
34
41
  export interface BetterAuthConfigOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0FAA0F;IAC1F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,uBAAuB;IACtC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,mHAAmH;AACnH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,GAAG,MAAM,CA4B/I"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0FAA0F;IAC1F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACjD;AAED,yCAAyC;AACzC,MAAM,WAAW,uBAAuB;IACtC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,mHAAmH;AACnH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,GAAG,MAAM,CA6B/I"}
package/dist/config.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * - `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`: the sign-in with Google.
8
8
  * - `AUTH_TEST_ACCOUNTS=1`: accounts with an email and a password, for the tests. Production refuses it.
9
9
  * - `PASSKEY_RP_ID`: the domain of the passkeys (default: the host of the first trusted origin).
10
+ * - `AUTH_CROSS_SITE_COOKIES=1`: the session cookies get `SameSite=None` and `Secure`, for a page on another site.
10
11
  */
11
12
  /** This function reads the configuration from the environment. It gives a message when the server cannot start. */
12
13
  export function betterAuthConfig(env, options) {
@@ -39,6 +40,7 @@ export function betterAuthConfig(env, options) {
39
40
  ...(google ? { google } : {}),
40
41
  testAccounts: env["AUTH_TEST_ACCOUNTS"] === "1",
41
42
  rpID: env["PASSKEY_RP_ID"] || firstHost,
43
+ ...(env["AUTH_CROSS_SITE_COOKIES"] === "1" ? { crossSiteCookies: true } : {}),
42
44
  };
43
45
  }
44
46
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsCH,mHAAmH;AACnH,MAAM,UAAU,gBAAgB,CAAC,GAAiD,EAAE,OAAgC;IAClH,MAAM,MAAM,GAAG,GAAG,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,oDAAoD,CAAC;IACpF,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,IAAI,GAAG,CAAC,oBAAoB,CAAC,KAAK,GAAG;QAAE,OAAO,6CAA6C,CAAC;IAChI,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,GAAW,EAA2B,EAAE;QAChE,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,CAAC,CAAC;IACF,IAAI,SAAS,GAAG,WAAW,CAAC;IAC5B,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,QAAQ,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAChE,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK;QACjC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO;QACrC,MAAM;QACN,OAAO,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,oBAAoB,OAAO,CAAC,IAAI,EAAE;QACnE,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,YAAY,EAAE,GAAG,CAAC,oBAAoB,CAAC,KAAK,GAAG;QAC/C,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,SAAS;KACxC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4CH,mHAAmH;AACnH,MAAM,UAAU,gBAAgB,CAAC,GAAiD,EAAE,OAAgC;IAClH,MAAM,MAAM,GAAG,GAAG,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,oDAAoD,CAAC;IACpF,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,IAAI,GAAG,CAAC,oBAAoB,CAAC,KAAK,GAAG;QAAE,OAAO,6CAA6C,CAAC;IAChI,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,GAAW,EAA2B,EAAE;QAChE,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,CAAC,CAAC;IACF,IAAI,SAAS,GAAG,WAAW,CAAC;IAC5B,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,QAAQ,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAChE,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK;QACjC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO;QACrC,MAAM;QACN,OAAO,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,oBAAoB,OAAO,CAAC,IAAI,EAAE;QACnE,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,YAAY,EAAE,GAAG,CAAC,oBAAoB,CAAC,KAAK,GAAG;QAC/C,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,SAAS;QACvC,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The Node handler of the auth routes, with credentialed CORS from the trusted origins of the
3
+ * host (CLI-49).
4
+ *
5
+ * The handler of Better Auth sends no CORS headers. A page on another origin then gets no
6
+ * session, and its POST requests fail. `betterAuthHandler()` puts the CORS of
7
+ * `credentialedCors()` of `@procedures/client/node` in front of the routes. Thus the
8
+ * `allowedOrigins` of the host, the list of the browser checks of `procedureHandler`, drives
9
+ * the CORS of the auth routes too. `betterAuthFace()` takes that list from the policy of a
10
+ * procedures host.
11
+ */
12
+ import type { IncomingMessage, ServerResponse } from "node:http";
13
+ import type { BrowserGuardOptions, HostPolicy } from "@procedures/client";
14
+ import { type CredentialedCorsOptions } from "@procedures/client/node";
15
+ import type { BetterAuthServer } from "./auth.js";
16
+ /** The methods of a preflight of the auth routes: Better Auth uses GET and POST. */
17
+ export declare const AUTH_CORS_METHODS: readonly string[];
18
+ /** The request headers of a preflight of the auth routes. */
19
+ export declare const AUTH_CORS_HEADERS: readonly string[];
20
+ /**
21
+ * The request tracking of a procedures host: `track` of the context of a face (CLI-54). The
22
+ * function does the work of one request through the service of the host, on the route
23
+ * `<face> <key>`. It answers a refusal with 503. The type is structural.
24
+ */
25
+ export type AuthRequestTracker = (req: IncomingMessage, res: ServerResponse, key: string, serve: (signal: AbortSignal) => Promise<void> | void) => Promise<void>;
26
+ /** The options of `betterAuthHandler()`. */
27
+ export interface BetterAuthHandlerOptions {
28
+ /**
29
+ * The origins that get credentialed CORS: the `allowedOrigins` of the host. A list or a
30
+ * function of origins. `"*"` is refused (CSRF). Without it, no other origin gets CORS.
31
+ */
32
+ readonly allowedOrigins?: BrowserGuardOptions["allowedOrigins"];
33
+ /** The path of the auth routes. The default is `/auth`, as `betterAuthConfig` gives it. */
34
+ readonly basePath?: string | undefined;
35
+ /**
36
+ * More of the CORS: `methods`, `allowedHeaders`, `exposedHeaders` and `maxAge`. The defaults are
37
+ * `AUTH_CORS_METHODS` and `AUTH_CORS_HEADERS`.
38
+ */
39
+ readonly cors?: CredentialedCorsOptions | undefined;
40
+ /**
41
+ * The request tracking of a host (CLI-54). Each request of the routes goes through it, with the
42
+ * path under `basePath` as its key. A preflight does not. `betterAuthFace()` gives the tracking
43
+ * of the host. Without it, each request starts directly.
44
+ */
45
+ readonly track?: AuthRequestTracker | undefined;
46
+ }
47
+ /**
48
+ * The Node handler of the auth routes. A call gives true when the handler takes the request (its
49
+ * path is under `basePath`), and false for another request. The handler then does not touch the
50
+ * request.
51
+ */
52
+ export interface NodeAuthHandler {
53
+ (req: IncomingMessage, res: ServerResponse): boolean;
54
+ /** This method gives true when the handler takes the request: its path is under `basePath`. */
55
+ matches(req: IncomingMessage): boolean;
56
+ /** The handler as a middleware of Connect or Express, at the level of the app: a request outside `basePath` goes to `next`. */
57
+ middleware(req: IncomingMessage, res: ServerResponse, next: (error?: unknown) => void): void;
58
+ /** The path under which the handler takes the requests. */
59
+ readonly basePath: string;
60
+ }
61
+ /**
62
+ * This function gives the Node handler of the auth routes, with credentialed CORS for the trusted
63
+ * origins. Mount it on the mount list of a listener, or as a middleware of the app.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const handler = betterAuthHandler(auth, { allowedOrigins: ["http://localhost:5173"] });
68
+ * app.use(handler.middleware); // Express, at the level of the app
69
+ * ```
70
+ */
71
+ export declare function betterAuthHandler(auth: Pick<BetterAuthServer, "handler">, options?: BetterAuthHandlerOptions): NodeAuthHandler;
72
+ /** What the auth face needs of its host: the policy, for its trusted origins, and the request tracking. */
73
+ export interface BetterAuthFaceContext {
74
+ readonly policy: HostPolicy;
75
+ /** The request tracking of the host, on the route pattern of the face (`auth *`, CLI-54). */
76
+ readonly track?: AuthRequestTracker | undefined;
77
+ }
78
+ /** The auth face: its request handler and its endpoint, for `host.info`. */
79
+ export interface BetterAuthFace {
80
+ readonly handler: (req: IncomingMessage, res: ServerResponse) => boolean;
81
+ readonly endpoint: {
82
+ readonly type: string;
83
+ readonly scheme: "http";
84
+ readonly path: string;
85
+ };
86
+ }
87
+ /**
88
+ * This function gives the auth routes as a face of a procedures host (`createProcedureHost()` of
89
+ * `@procedures/peer`). The face takes the trusted origins of the policy of the host
90
+ * (`browser.allowedOrigins`), unless the options give other origins. Each request of the routes
91
+ * goes through the request tracking of the host (`track`, CLI-54). Thus a request after the stop
92
+ * of the intake gets 503, and the drain waits for a request in flight. The types are structural:
93
+ * this package does not depend on `@procedures/peer`.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const host = createProcedureHost({ registry, policy, faces: { http: true, auth: betterAuthFace(auth) }, listen });
98
+ * ```
99
+ */
100
+ export declare function betterAuthFace(auth: Pick<BetterAuthServer, "handler">, options?: BetterAuthHandlerOptions): (context: BetterAuthFaceContext) => BetterAuthFace;
101
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAA8C,CAAC;AAE9F,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAqD,CAAC;AAErG;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,KACjD,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,4CAA4C;AAC5C,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;IAChE,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;IACrD,+FAA+F;IAC/F,OAAO,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;IACvC,+HAA+H;IAC/H,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7F,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAgCD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,eAAe,CA8ClI;AAED,2GAA2G;AAC3G,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACjD;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;IACzE,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,cAAc,CAalK"}
@@ -0,0 +1,122 @@
1
+ import { toNodeHandler } from "better-auth/node";
2
+ import { credentialedCors } from "@procedures/client/node";
3
+ /** The methods of a preflight of the auth routes: Better Auth uses GET and POST. */
4
+ export const AUTH_CORS_METHODS = Object.freeze(["GET", "POST", "OPTIONS"]);
5
+ /** The request headers of a preflight of the auth routes. */
6
+ export const AUTH_CORS_HEADERS = Object.freeze(["Content-Type", "Authorization"]);
7
+ /** True when the value of a `Vary` header names the field, or is `*`. */
8
+ function varies(value, field) {
9
+ return value.split(",").some((part) => {
10
+ const name = part.trim().toLowerCase();
11
+ return name === "*" || name === field.toLowerCase();
12
+ });
13
+ }
14
+ /**
15
+ * The handler of the routes, with `Origin` in each `Vary` header. The Node adapter of Better Auth
16
+ * sets each header of a response with `setHeader`, so a `Vary` of a route replaces the `Vary:
17
+ * Origin` of the CORS. A shared cache then can give the answer of one origin to another origin.
18
+ */
19
+ function keepVaryOrigin(handler) {
20
+ return async (request) => {
21
+ const response = await handler(request);
22
+ const vary = response.headers.get("vary");
23
+ if (vary === null || varies(vary, "Origin"))
24
+ return response;
25
+ const headers = new Headers(response.headers);
26
+ headers.set("vary", `${vary}, Origin`);
27
+ return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
28
+ };
29
+ }
30
+ /** The path of a request URL, without its query. */
31
+ function pathOf(url) {
32
+ const path = (url ?? "/").split("?")[0] ?? "/";
33
+ return path === "" ? "/" : path;
34
+ }
35
+ /**
36
+ * This function gives the Node handler of the auth routes, with credentialed CORS for the trusted
37
+ * origins. Mount it on the mount list of a listener, or as a middleware of the app.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const handler = betterAuthHandler(auth, { allowedOrigins: ["http://localhost:5173"] });
42
+ * app.use(handler.middleware); // Express, at the level of the app
43
+ * ```
44
+ */
45
+ export function betterAuthHandler(auth, options = {}) {
46
+ const basePath = (options.basePath ?? "/auth").replace(/\/+$/, "") || "/";
47
+ const prefix = basePath === "/" ? "/" : `${basePath}/`;
48
+ const cors = credentialedCors(options.allowedOrigins, { methods: AUTH_CORS_METHODS, allowedHeaders: AUTH_CORS_HEADERS, ...options.cors });
49
+ // The CORS sets `Vary: Origin` on each response, for each origin: the routes keep it
50
+ const routes = toNodeHandler(keepVaryOrigin(auth.handler));
51
+ const matches = (req) => {
52
+ const path = pathOf(req.url);
53
+ return path === basePath || path.startsWith(prefix);
54
+ };
55
+ const fail = (res, error) => {
56
+ if (res.headersSent) {
57
+ res.destroy(error instanceof Error ? error : new Error(String(error)));
58
+ return;
59
+ }
60
+ res.statusCode = 500;
61
+ res.setHeader("Content-Type", "application/json");
62
+ res.end(JSON.stringify({ error: "The auth routes failed", code: "INTERNAL_ERROR" }));
63
+ };
64
+ const { track } = options;
65
+ const serve = (req, res) => {
66
+ // A preflight ends in the CORS. A request of another origin goes on without CORS headers:
67
+ // the CSRF checks of Better Auth refuse a write of an origin that it does not trust
68
+ if (cors(req, res))
69
+ return;
70
+ if (!track) {
71
+ routes(req, res).catch((error) => fail(res, error));
72
+ return;
73
+ }
74
+ // The tracking of the host: the request counts until its response closes, and the drain waits
75
+ // for it. The CORS headers are on the response already, so a page can read a refusal (503).
76
+ const path = pathOf(req.url);
77
+ const key = path.length > prefix.length ? path.slice(prefix.length) : "/";
78
+ track(req, res, key, () => routes(req, res)).catch((error) => fail(res, error));
79
+ };
80
+ const handler = (req, res) => {
81
+ if (!matches(req))
82
+ return false;
83
+ serve(req, res);
84
+ return true;
85
+ };
86
+ return Object.assign(handler, {
87
+ matches,
88
+ middleware: (req, res, next) => {
89
+ if (!handler(req, res))
90
+ next();
91
+ },
92
+ basePath,
93
+ });
94
+ }
95
+ /**
96
+ * This function gives the auth routes as a face of a procedures host (`createProcedureHost()` of
97
+ * `@procedures/peer`). The face takes the trusted origins of the policy of the host
98
+ * (`browser.allowedOrigins`), unless the options give other origins. Each request of the routes
99
+ * goes through the request tracking of the host (`track`, CLI-54). Thus a request after the stop
100
+ * of the intake gets 503, and the drain waits for a request in flight. The types are structural:
101
+ * this package does not depend on `@procedures/peer`.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const host = createProcedureHost({ registry, policy, faces: { http: true, auth: betterAuthFace(auth) }, listen });
106
+ * ```
107
+ */
108
+ export function betterAuthFace(auth, options = {}) {
109
+ return (context) => {
110
+ const track = options.track ?? context.track;
111
+ const handler = betterAuthHandler(auth, {
112
+ ...options,
113
+ allowedOrigins: options.allowedOrigins ?? context.policy.browser.allowedOrigins,
114
+ ...(track ? { track } : {}),
115
+ });
116
+ return {
117
+ handler: (req, res) => handler(req, res),
118
+ endpoint: { type: "auth", scheme: "http", path: handler.basePath },
119
+ };
120
+ };
121
+ }
122
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from "./config.js";
3
3
  export * from "./auth.js";
4
4
  export * from "./principals.js";
5
5
  export * from "./principal-of.js";
6
+ export * from "./handler.js";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,5 @@ export * from "./config.js";
3
3
  export * from "./auth.js";
4
4
  export * from "./principals.js";
5
5
  export * from "./principal-of.js";
6
+ export * from "./handler.js";
6
7
  //# 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,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The options of Better Auth for a configuration and a database adapter. `createBetterAuth` gives
3
+ * them the MongoDB adapter, and a test gives them the memory adapter. The package root does not
4
+ * export this module.
5
+ */
6
+ import type { BetterAuthOptions } from "better-auth";
7
+ import type { BetterAuthConfig } from "./config.js";
8
+ /** This function gives the options of Better Auth for a configuration and a database adapter. */
9
+ export declare function betterAuthOptions(config: BetterAuthConfig, database: BetterAuthOptions["database"]): BetterAuthOptions;
10
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,GAAG,iBAAiB,CA2BtH"}
@@ -0,0 +1,32 @@
1
+ import { organization } from "better-auth/plugins";
2
+ import { passkey } from "@better-auth/passkey";
3
+ /** This function gives the options of Better Auth for a configuration and a database adapter. */
4
+ export function betterAuthOptions(config, database) {
5
+ const testHooks = {
6
+ user: {
7
+ create: {
8
+ before: async (user) => ({ data: { ...user, emailVerified: true } }),
9
+ },
10
+ },
11
+ };
12
+ return {
13
+ appName: config.appName,
14
+ basePath: config.basePath,
15
+ baseURL: config.baseURL,
16
+ secret: config.secret,
17
+ database: database,
18
+ trustedOrigins: [...config.trustedOrigins],
19
+ socialProviders: {
20
+ ...(config.github ? { github: { ...config.github } } : {}),
21
+ ...(config.google ? { google: { ...config.google } } : {}),
22
+ },
23
+ emailAndPassword: { enabled: config.testAccounts },
24
+ // A test account has no email check: the test server trusts its email
25
+ ...(config.testAccounts ? { databaseHooks: testHooks } : {}),
26
+ // A page on another site sends a cookie only with SameSite=None, and a browser keeps such a cookie only when it is Secure (CLI-49)
27
+ ...(config.crossSiteCookies === true ? { advanced: { defaultCookieAttributes: { sameSite: "none", secure: true } } } : {}),
28
+ // The passkeys expect the origins of the app, not the Origin header of each request
29
+ plugins: [organization(), passkey({ rpID: config.rpID, rpName: config.appName, origin: [...config.trustedOrigins] })],
30
+ };
31
+ }
32
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,iGAAiG;AACjG,MAAM,UAAU,iBAAiB,CAAC,MAAwB,EAAE,QAAuC;IACjG,MAAM,SAAS,GAAG;QAChB,IAAI,EAAE;YACJ,MAAM,EAAE;gBACN,MAAM,EAAE,KAAK,EAAE,IAA6B,EAA8C,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;aAC1I;SACF;KACF,CAAC;IACF,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,QAAQ;QAClB,cAAc,EAAE,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC;QAC1C,eAAe,EAAE;YACf,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D;QACD,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE;QAClD,sEAAsE;QACtE,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,mIAAmI;QACnI,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,uBAAuB,EAAE,EAAE,QAAQ,EAAE,MAAe,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnI,oFAAoF;QACpF,OAAO,EAAE,CAAC,YAAY,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;KACtH,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@procedures/better-auth",
4
- "version": "0.13.2",
4
+ "version": "0.14.1",
5
5
  "description": "Better Auth for a procedures server: the sign-in with GitHub, Google and passkeys over MongoDB, and a request hook that gives the principal of a session or of an agent token.",
6
6
  "keywords": [
7
7
  "procedures",
@@ -50,10 +50,14 @@
50
50
  "engines": {
51
51
  "node": ">=22"
52
52
  },
53
- "client": null,
53
+ "client": {
54
+ "browser": [
55
+ "./browser"
56
+ ]
57
+ },
54
58
  "dependencies": {
55
59
  "@better-auth/passkey": "^1.7.10",
56
- "@procedures/client": "0.13.2",
60
+ "@procedures/client": "0.14.1",
57
61
  "@simplewebauthn/browser": "^13.3.0",
58
62
  "better-auth": "^1.7.10",
59
63
  "zod": "^4.6.5"
package/src/auth.ts CHANGED
@@ -4,18 +4,18 @@
4
4
  * password. Better Auth does the OAuth state, PKCE and the CSRF checks.
5
5
  *
6
6
  * Give Better Auth its own database: its `user` collection must not mix with the data of the app.
7
- * The app mounts `auth.handler` on `config.basePath`: the routes belong to the app.
7
+ * The app mounts `auth.handler` on `config.basePath` (`betterAuthHandler()`): the routes belong to the app.
8
8
  */
9
9
  import { betterAuth } from "better-auth";
10
10
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
11
- import { organization } from "better-auth/plugins";
12
- import { passkey } from "@better-auth/passkey";
13
11
  import type { Db, MongoClient } from "mongodb";
14
12
  import type { BetterAuthConfig } from "./config.js";
13
+ import { betterAuthOptions } from "./options.js";
15
14
 
16
15
  /**
17
- * The parts of a Better Auth server that an app uses. Mount `handler` on `config.basePath`, for
18
- * example with `toNodeHandler` of `better-auth/node`. `SessionPrincipals` reads `api.getSession`.
16
+ * The parts of a Better Auth server that an app uses. Mount `handler` on `config.basePath` with
17
+ * `betterAuthHandler()`, which adds the credentialed CORS of the trusted origins. `SessionPrincipals`
18
+ * reads `api.getSession`.
19
19
  */
20
20
  export interface BetterAuthServer {
21
21
  /** The handler of the auth routes. */
@@ -28,28 +28,5 @@ export interface BetterAuthServer {
28
28
 
29
29
  /** This function makes the Better Auth server of a configuration. */
30
30
  export function createBetterAuth(db: Db, client: MongoClient, config: BetterAuthConfig): BetterAuthServer {
31
- const testHooks = {
32
- user: {
33
- create: {
34
- before: async (user: Record<string, unknown>): Promise<{ data: Record<string, unknown> }> => ({ data: { ...user, emailVerified: true } }),
35
- },
36
- },
37
- };
38
- return betterAuth({
39
- appName: config.appName,
40
- basePath: config.basePath,
41
- baseURL: config.baseURL,
42
- secret: config.secret,
43
- database: mongodbAdapter(db, { client }),
44
- trustedOrigins: [...config.trustedOrigins],
45
- socialProviders: {
46
- ...(config.github ? { github: { ...config.github } } : {}),
47
- ...(config.google ? { google: { ...config.google } } : {}),
48
- },
49
- emailAndPassword: { enabled: config.testAccounts },
50
- // A test account has no email check: the test server trusts its email
51
- ...(config.testAccounts ? { databaseHooks: testHooks } : {}),
52
- // The passkeys expect the origins of the app, not the Origin header of each request
53
- plugins: [organization(), passkey({ rpID: config.rpID, rpName: config.appName, origin: [...config.trustedOrigins] })],
54
- });
31
+ return betterAuth(betterAuthOptions(config, mongodbAdapter(db, { client })));
55
32
  }
package/src/config.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * - `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`: the sign-in with Google.
8
8
  * - `AUTH_TEST_ACCOUNTS=1`: accounts with an email and a password, for the tests. Production refuses it.
9
9
  * - `PASSKEY_RP_ID`: the domain of the passkeys (default: the host of the first trusted origin).
10
+ * - `AUTH_CROSS_SITE_COOKIES=1`: the session cookies get `SameSite=None` and `Secure`, for a page on another site.
10
11
  */
11
12
 
12
13
  /** The client id and the secret of an OAuth provider. */
@@ -31,6 +32,12 @@ export interface BetterAuthConfig {
31
32
  readonly testAccounts: boolean;
32
33
  /** The relying party of the passkeys: a domain of the origins of the app. */
33
34
  readonly rpID: string;
35
+ /**
36
+ * True: the cookies of Better Auth get `SameSite=None` and `Secure`, so a page on another site
37
+ * sends them (CLI-49). A browser keeps a `Secure` cookie only over HTTPS or from `localhost`.
38
+ * The default is false: `SameSite=Lax`.
39
+ */
40
+ readonly crossSiteCookies?: boolean | undefined;
34
41
  }
35
42
 
36
43
  /** The options of `betterAuthConfig`. */
@@ -73,5 +80,6 @@ export function betterAuthConfig(env: Readonly<Record<string, string | undefined
73
80
  ...(google ? { google } : {}),
74
81
  testAccounts: env["AUTH_TEST_ACCOUNTS"] === "1",
75
82
  rpID: env["PASSKEY_RP_ID"] || firstHost,
83
+ ...(env["AUTH_CROSS_SITE_COOKIES"] === "1" ? { crossSiteCookies: true } : {}),
76
84
  };
77
85
  }