@sanoma-ai/connector-better-auth 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,105 @@
1
+ # Functional Source License, Version 1.1, ALv2 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-ALv2
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Sanoma-AI
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the Apache License, Version 2.0 that is effective on the second anniversary of
91
+ the date we make the Software available. On or after that date, you may use the
92
+ Software under the Apache License, Version 2.0, in which case the following
93
+ will apply:
94
+
95
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
96
+ this file except in compliance with the License.
97
+
98
+ You may obtain a copy of the License at
99
+
100
+ http://www.apache.org/licenses/LICENSE-2.0
101
+
102
+ Unless required by applicable law or agreed to in writing, software distributed
103
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
+ specific language governing permissions and limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,65 @@
1
- # Temporary Holding Version
1
+ # @sanoma-ai/connector-better-auth
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [Better Auth](https://www.better-auth.com) as a visitor login for a [Sanoma](https://github.com/Sanoma-AI/sanoma) public site: visitors sign up and sign in with an email and a password, and their accounts live on the company's own database. It has no operations and no credential; it attaches a login half (`withSetup({ login })`, see [`@sanoma-ai/workflows`](https://www.npmjs.com/package/@sanoma-ai/workflows#usage)), which the app calls once for each public site that names it.
4
+
5
+ ## Usage
6
+
7
+ ```sh
8
+ npm install @sanoma-ai/connector-better-auth
9
+ ```
10
+
11
+ ```ts
12
+ // sanoma.config.ts
13
+ import { betterAuth } from "@sanoma-ai/connector-better-auth";
14
+
15
+ export default defineConfig({ workflows, connectors: [betterAuth] });
16
+ ```
17
+
18
+ ```ts
19
+ // sites/account/site.ts
20
+ import { defineSite } from "sanoma/site";
21
+
22
+ export default defineSite({ title: "Account", public: true, login: "better-auth" });
23
+ ```
24
+
25
+ A visitor who signs in is the principal `visitor:better-auth:<id>`, in the group `visitors`, where `<id>` is Better Auth's user id; the app makes the principal, never the connector. What a visitor may do is a Cedar `permit` naming that group:
26
+
27
+ ```cedar
28
+ // policies/visitors.cedar
29
+ permit (principal in Sanoma::Group::"visitors", action == Sanoma::Action::"workflow:hello", resource);
30
+ ```
31
+
32
+ ### No secret to set
33
+
34
+ It has no credential. Better Auth signs its cookies with a key the app derives for this login from the deployment's `SANOMA_SECRETS_KEY` (HKDF-SHA256, purpose `login better-auth`), which `sanoma` already sets up; changing that key signs every visitor out.
35
+
36
+ ### The schema
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.
39
+
40
+ ### The routes
41
+
42
+ The login answers under `/auth/better-auth/` on the site's own host, and nothing else. A site's pages are its sign-in forms, drawn from the kit (`sanoma/ui`), that `fetch` JSON with a POST (the browser adds `Origin`, which Better Auth checks):
43
+
44
+ | Route | Body | What it does |
45
+ | -------------------------------------- | ----------------------------- | ------------------------------------------------------------------------ |
46
+ | `POST /auth/better-auth/sign-up/email` | `{ name, email, password }` | Makes the account and signs the visitor in (8 characters or more). |
47
+ | `POST /auth/better-auth/sign-in/email` | `{ email, password }` | Signs in; a wrong email or password is 401. |
48
+ | `POST /auth/better-auth/sign-out` | `{}`, or none (the app's own) | Ends the session; it is also `signOut`, which `useActor()` gives a page. |
49
+
50
+ ```ts
51
+ const response = await fetch("/auth/better-auth/sign-in/email", {
52
+ method: "POST",
53
+ headers: { "content-type": "application/json" },
54
+ body: JSON.stringify({ email, password }),
55
+ });
56
+ if (response.ok) window.location.reload(); // the app reads the new session cookie
57
+ ```
58
+
59
+ 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.
60
+
61
+ ### Later
62
+
63
+ Email verification and password reset (they need a sender, such as Resend), social providers (Google, GitHub), and the rate limit kept in the database for several processes.
64
+
65
+ 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.
@@ -0,0 +1,2 @@
1
+ /** Better Auth: no operations, no driver and no credential; its login half is in `login.ts`. */
2
+ export declare const betterAuthOps: import("@sanoma-ai/workflows").Connector<"better-auth", {}>;
@@ -0,0 +1,4 @@
1
+ import { defineConnector, vendorInfo } from "@sanoma-ai/workflows";
2
+ /** Better Auth: no operations, no driver and no credential; its login half is in `login.ts`. */
3
+ export const betterAuthOps = defineConnector("better-auth", {}, vendorInfo(import.meta.url));
4
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,gGAAgG;AAChG,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC,aAAa,EAAE,EAAE,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { betterAuthLogin } from "./login.ts";
2
+ /**
3
+ * Better Auth, a visitor login for public sites (`defineSite({ public: true, login: "better-auth" })`):
4
+ * email and password, its users in schema `better_auth` on the company's database, its routes
5
+ * under `/auth/better-auth/`, its cookies signed with the key the app derives for it.
6
+ */
7
+ export declare const betterAuth: import("@sanoma-ai/workflows").Connector<"better-auth", {}>;
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { withSetup } from "@sanoma-ai/workflows";
2
+ import { betterAuthOps } from "./connector.js";
3
+ import { betterAuthLogin } from "./login.js";
4
+ export { betterAuthLogin } from "./login.js";
5
+ /**
6
+ * Better Auth, a visitor login for public sites (`defineSite({ public: true, login: "better-auth" })`):
7
+ * email and password, its users in schema `better_auth` on the company's database, its routes
8
+ * under `/auth/better-auth/`, its cookies signed with the key the app derives for it.
9
+ */
10
+ export const betterAuth = withSetup(betterAuthOps, { login: betterAuthLogin });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type LoginContext, type LoginHalf } from "@sanoma-ai/workflows";
2
+ /**
3
+ * Better Auth's login half for one public site: email and password, its tables in schema
4
+ * `better_auth` on the company's database, migrated here before the first site starts, its cookies
5
+ * signed with the app's key for it (`context.key`). The site's origins are its allowed hosts (a
6
+ * request for another host is refused) and set the cookie's `Secure` flag: an `https` origin's is.
7
+ */
8
+ export declare function betterAuthLogin({ databaseUrl, key, origins }: LoginContext): Promise<LoginHalf>;
package/dist/login.js ADDED
@@ -0,0 +1,140 @@
1
+ import { CLIENT_IP_HEADER } from "@sanoma-ai/workflows";
2
+ import { betterAuth, getHostFromSource, matchesHostPattern } from "better-auth";
3
+ import { getMigrations } from "better-auth/db/migration";
4
+ import { PostgresDialect } from "kysely";
5
+ import { setTimeout as delay } from "node:timers/promises";
6
+ import { Client, Pool } from "pg";
7
+ /** The schema Better Auth's tables (`user`, `session`, `account`, `verification`) live in, on the company's database. */
8
+ const SCHEMA = "better_auth";
9
+ /** Where its routes are: a site's pages POST to `<BASE_PATH>/sign-up/email`, `/sign-in/email` and `/sign-out`. */
10
+ const BASE_PATH = "/auth/better-auth";
11
+ const SIGN_OUT = `${BASE_PATH}/sign-out`;
12
+ /** The advisory lock every process takes around the migrations: concurrent cold starts race otherwise. */
13
+ const LOCK = "sanoma.login.better-auth";
14
+ /** Each database's migration, once per process however many sites sign in through it. */
15
+ const migrated = new Map();
16
+ /**
17
+ * Better Auth's login half for one public site: email and password, its tables in schema
18
+ * `better_auth` on the company's database, migrated here before the first site starts, its cookies
19
+ * signed with the app's key for it (`context.key`). The site's origins are its allowed hosts (a
20
+ * request for another host is refused) and set the cookie's `Secure` flag: an `https` origin's is.
21
+ */
22
+ export async function betterAuthLogin({ databaseUrl, key, origins }) {
23
+ // Never Better Auth's own fallback (an environment variable, or its default secret).
24
+ if (!/^[0-9a-f]{64}$/.test(key ?? ""))
25
+ throw new Error("better-auth: the app gives no key to sign cookies with");
26
+ const sites = sitesOf(origins);
27
+ const pool = new Pool({ connectionString: databaseUrl });
28
+ pool.on("error", (err) => console.warn(`better-auth: a database connection failed: ${err.message}`));
29
+ // One Better Auth per protocol, since its cookies' `Secure` flag is one per instance.
30
+ const optionsFor = (protocol) => ({
31
+ // Schema-qualified queries, not a search_path, which the URL's own `options` could undo.
32
+ database: { dialect: new PostgresDialect({ pool }), type: "postgres", schemaName: SCHEMA, transaction: true },
33
+ // `secrets` over `secret`, else BETTER_AUTH_SECRETS in the environment would win.
34
+ secrets: [{ version: 1, value: key }],
35
+ secret: key,
36
+ basePath: BASE_PATH,
37
+ // A request's Host must be one of these, and its origin is trusted.
38
+ baseURL: { allowedHosts: sites.filter((site) => site.protocol === protocol).map((site) => site.host), protocol },
39
+ emailAndPassword: { enabled: true },
40
+ telemetry: { enabled: false },
41
+ advanced: {
42
+ // The app sets it from the socket; Better Auth would otherwise believe a client's X-Forwarded-For.
43
+ ipAddress: { ipAddressHeaders: [CLIENT_IP_HEADER] },
44
+ useSecureCookies: protocol === "https",
45
+ // Its default under NODE_ENV=test is off: the same check everywhere.
46
+ disableOriginCheck: false,
47
+ },
48
+ // On only in production by default; in memory, per process.
49
+ rateLimit: { enabled: true },
50
+ logger: { level: "warn" },
51
+ });
52
+ try {
53
+ const protocols = [...new Set(sites.map((site) => site.protocol))];
54
+ await migrateOnce(databaseUrl, optionsFor(protocols[0]));
55
+ // After the migrations: its start checks the tables, and says the schema is out of date otherwise.
56
+ const auths = new Map(protocols.map((protocol) => [protocol, betterAuth(optionsFor(protocol))]));
57
+ /** The instance for the request's host, or none for a host that is not the site's. */
58
+ const authFor = (request) => {
59
+ const host = getHostFromSource(request, false) ?? "";
60
+ const site = sites.find((s) => matchesHostPattern(host, s.host));
61
+ return site && auths.get(site.protocol);
62
+ };
63
+ return {
64
+ async verify(request) {
65
+ const auth = authFor(request);
66
+ if (!auth)
67
+ throw new Error(`better-auth: ${request.headers.get("host")} is not one of the site's hosts`);
68
+ // Null for a missing, bad or signed-out cookie.
69
+ const session = await auth.api.getSession({ headers: request.headers });
70
+ return session ? { subject: session.user.id } : null;
71
+ },
72
+ async handle(request) {
73
+ const { pathname } = new URL(request.url);
74
+ if (pathname !== BASE_PATH && !pathname.startsWith(`${BASE_PATH}/`))
75
+ return undefined;
76
+ // Better Auth throws for a host it does not allow, which would be a 500.
77
+ const auth = authFor(request);
78
+ if (!auth)
79
+ return new Response("Misdirected Request", { status: 421 });
80
+ // The app's Sign out POSTs no body, which Better Auth's sign-out answers with 415.
81
+ if (pathname === SIGN_OUT && request.method === "POST" && !request.headers.has("content-type")) {
82
+ const headers = new Headers(request.headers);
83
+ headers.set("content-type", "application/json");
84
+ headers.delete("content-length");
85
+ return auth.handler(new Request(request.url, { method: "POST", headers, body: "{}" }));
86
+ }
87
+ return auth.handler(request);
88
+ },
89
+ signOut: SIGN_OUT,
90
+ close: () => pool.end(),
91
+ };
92
+ }
93
+ catch (err) {
94
+ await pool.end();
95
+ throw err;
96
+ }
97
+ }
98
+ /** Each origin's host, as Better Auth's `allowedHosts` has it (`account.localhost:*`), and its protocol. */
99
+ function sitesOf(origins) {
100
+ if (!origins.length)
101
+ throw new Error("better-auth: a site needs an origin");
102
+ return origins.map((origin) => {
103
+ const [, protocol, host] = /^(https?):\/\/([^/]+)$/.exec(origin) ?? [];
104
+ if (!host)
105
+ throw new Error(`better-auth: an origin is a scheme and a host alone, such as https://example.com, not ${origin}`);
106
+ return { host, protocol: protocol };
107
+ });
108
+ }
109
+ /** Better Auth's migrations, once per database in this process, under the advisory lock across processes. */
110
+ function migrateOnce(databaseUrl, options) {
111
+ let done = migrated.get(databaseUrl);
112
+ if (!done) {
113
+ done = migrate(databaseUrl, options).catch((err) => {
114
+ migrated.delete(databaseUrl);
115
+ throw err;
116
+ });
117
+ migrated.set(databaseUrl, done);
118
+ }
119
+ return done;
120
+ }
121
+ /**
122
+ * Holds the lock on a session of its own until that ends, whatever fails; polled, never waited
123
+ * for by a blocked statement, as the runtime's own preparation does. The migrations make the schema.
124
+ */
125
+ async function migrate(databaseUrl, options) {
126
+ const conn = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 10_000 });
127
+ // An error nobody listens for ends the process; the query in flight fails with it.
128
+ conn.on("error", (err) => console.warn(`better-auth: the migration's connection failed: ${err.message}`));
129
+ await conn.connect();
130
+ try {
131
+ const ask = "SELECT pg_try_advisory_lock(hashtext($1)) AS locked";
132
+ while (!(await conn.query(ask, [LOCK])).rows[0].locked)
133
+ await delay(50);
134
+ await (await getMigrations(options)).runMigrations();
135
+ }
136
+ finally {
137
+ await conn.end().catch(() => undefined);
138
+ }
139
+ }
140
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
package/logo-dark.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 45"><path fill="white" fill-rule="evenodd" d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z"/></svg>
package/logo.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 45"><path fill="black" fill-rule="evenodd" d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z"/></svg>
package/package.json CHANGED
@@ -1,6 +1,68 @@
1
1
  {
2
2
  "name": "@sanoma-ai/connector-better-auth",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Better Auth as a visitor login for Sanoma's public sites: email and password, on the company's own database",
5
+ "keywords": [
6
+ "auth",
7
+ "better-auth",
8
+ "connector",
9
+ "login",
10
+ "sanoma",
11
+ "sanoma-connector"
12
+ ],
13
+ "homepage": "https://github.com/Sanoma-AI/sanoma/tree/main/connectors/better-auth#readme",
14
+ "bugs": "https://github.com/Sanoma-AI/sanoma/issues",
15
+ "license": "FSL-1.1-ALv2",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Sanoma-AI/sanoma.git",
19
+ "directory": "connectors/better-auth"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "logo.svg",
24
+ "logo-dark.svg",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "better-auth": "~1.7.12",
43
+ "kysely": "^0.29.7",
44
+ "pg": "^8.23.1"
45
+ },
46
+ "devDependencies": {
47
+ "@sanoma-ai/app": "0.5.0",
48
+ "@sanoma-ai/testing": "0.1.0",
49
+ "@sanoma-ai/workflows": "0.5.0",
50
+ "@types/pg": "^8.23.1"
51
+ },
52
+ "peerDependencies": {
53
+ "@sanoma-ai/workflows": "^0.5.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=24"
57
+ },
58
+ "sanoma": {
59
+ "title": "Better Auth",
60
+ "logo": "logo.svg",
61
+ "logoDark": "logo-dark.svg"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc -p tsconfig.build.json",
65
+ "clean": "rm -rf dist",
66
+ "watch": "tsc -p tsconfig.build.json --watch --preserveWatchOutput"
67
+ }
6
68
  }