@substrat-run/vertical-auth 0.6.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-do.js","sourceRoot":"","sources":["../src/identity-do.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type { AuthProvider, AuthSubject } from './provider.js';
2
+ export { oidcAuthProvider, type OidcConfig } from './oidc.js';
3
+ export { oidcRpAuthProvider, type OidcRpConfig } from './oidc-rp-provider.js';
4
+ export { IdentityDO, doAuthProvider, type IdentityDoEnv, type IdentityStub } from './identity-do.js';
5
+ export { resolveCookieDomain } from './cookie-domain.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { oidcAuthProvider } from './oidc.js';
2
+ export { oidcRpAuthProvider } from './oidc-rp-provider.js';
3
+ export { IdentityDO, doAuthProvider } from './identity-do.js';
4
+ export { resolveCookieDomain } from './cookie-domain.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAmB,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAqB,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAyC,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { AuthProvider } from './provider.js';
2
+ /**
3
+ * Standard OIDC as a full RELYING PARTY `AuthProvider` — the browser-login counterpart
4
+ * of `oidcAuthProvider` (which only verifies presented bearer tokens). `handle` owns the
5
+ * server-side Authorization-Code + PKCE round-trip on the same paths every provider uses
6
+ * (`/api/auth/login` → issuer → `/api/auth/callback` → session cookie → `/api/auth/logout`),
7
+ * and `resolve` verifies the session cookie — falling back to bearer verification so API
8
+ * clients keep working against the same instance.
9
+ *
10
+ * Config is a PLAIN OBJECT, not worker env: a hosted vertical builds it per request from
11
+ * the scope's delivered `substrat:auth` (vertical-auth-detach.md §2.2-§2.3) plus the
12
+ * tenant's DO-minted session secret — one script, many issuers. The `oidc-rp` internals
13
+ * already take their env as a parameter everywhere, so this is the same battle-tested
14
+ * flow the dashboard and console run, pointed at per-instance config.
15
+ */
16
+ export interface OidcRpConfig {
17
+ /** The issuer origin — a team's Auth Server app, Supabase, Auth0, Keycloak, … */
18
+ issuer: string;
19
+ clientId: string;
20
+ clientSecret: string;
21
+ /** Signs the flow + session cookies. Per-instance (DO-minted), never a worker binding. */
22
+ sessionSecret: string;
23
+ /** Expected `aud` for PRESENTED bearer tokens (the API-client path), if the issuer sets one. */
24
+ audience?: string;
25
+ /**
26
+ * Share the login across every surface under this parent domain (`egeryds.se` covers
27
+ * `crm.` and `eka.` alike) — the session cookie is set with `Domain=…` instead of
28
+ * host-only. Delivered per scope (`substrat:auth`), validated against the request host
29
+ * where the cookie is set (cookie-domain.ts); invalid ⇒ host-only, never broken sign-in.
30
+ */
31
+ cookieDomain?: string;
32
+ }
33
+ export declare function oidcRpAuthProvider(cfg: OidcRpConfig): AuthProvider;
34
+ //# sourceMappingURL=oidc-rp-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-rp-provider.d.ts","sourceRoot":"","sources":["../src/oidc-rp-provider.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,eAAe,CAAC;AAI/D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,aAAa,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsDD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,CA2DlE"}
@@ -0,0 +1,107 @@
1
+ import { beginLogin, completeLogin, verifySession, readCookie, safePath, SESSION_COOKIE, FLOW_COOKIE, SESSION_MAXAGE, FLOW_MAXAGE, } from '@substrat-run/oidc-rp';
2
+ import { oidcAuthProvider } from './oidc.js';
3
+ import { resolveCookieDomain } from './cookie-domain.js';
4
+ const envOf = (cfg) => ({
5
+ OIDC_ISSUER: cfg.issuer,
6
+ OIDC_CLIENT_ID: cfg.clientId,
7
+ OIDC_CLIENT_SECRET: cfg.clientSecret,
8
+ SESSION_SECRET: cfg.sessionSecret,
9
+ });
10
+ /** Serialize one Set-Cookie value — HttpOnly, Lax, path=/ (the oidc-rp mount's flags). */
11
+ function cookie(name, value, origin, maxAge, domain) {
12
+ const parts = [
13
+ `${name}=${encodeURIComponent(value)}`,
14
+ 'Path=/',
15
+ `Max-Age=${maxAge}`,
16
+ 'HttpOnly',
17
+ 'SameSite=Lax',
18
+ ];
19
+ if (domain)
20
+ parts.push(`Domain=${domain}`);
21
+ if (origin.startsWith('https:'))
22
+ parts.push('Secure');
23
+ return parts.join('; ');
24
+ }
25
+ /**
26
+ * Set the session cookie, host-only or domain-wide. A domain cookie and a host-only
27
+ * cookie with the same name are DISTINCT cookies to the browser, and a stale host-only
28
+ * one (from before `cookieDomain` was configured) would shadow the shared session on
29
+ * this hostname — so setting the domain variant also clears the host-only one, and
30
+ * clearing (logout) always clears both.
31
+ */
32
+ function sessionCookies(value, origin, maxAge, domain) {
33
+ if (!domain)
34
+ return [cookie(SESSION_COOKIE, value, origin, maxAge)];
35
+ return [cookie(SESSION_COOKIE, value, origin, maxAge, domain), cookie(SESSION_COOKIE, '', origin, 0)];
36
+ }
37
+ function redirectWith(location, cookies) {
38
+ const headers = new Headers({ location });
39
+ for (const c of cookies)
40
+ headers.append('set-cookie', c);
41
+ return new Response(null, { status: 302, headers });
42
+ }
43
+ // Bearer verifiers cached per issuer+audience so the JWKS fetch survives across
44
+ // requests in an isolate even though the provider object itself is per-request.
45
+ const bearerCache = new Map();
46
+ function bearerVerifier(cfg) {
47
+ const key = `${cfg.issuer}|${cfg.audience ?? ''}`;
48
+ let p = bearerCache.get(key);
49
+ if (!p) {
50
+ p = oidcAuthProvider({ issuer: cfg.issuer, ...(cfg.audience ? { audience: cfg.audience } : {}) });
51
+ bearerCache.set(key, p);
52
+ }
53
+ return p;
54
+ }
55
+ export function oidcRpAuthProvider(cfg) {
56
+ const env = envOf(cfg);
57
+ async function handle(request) {
58
+ const url = new URL(request.url);
59
+ const origin = url.origin;
60
+ // The flow cookie stays HOST-ONLY even with a cookieDomain: the code flow begins and
61
+ // completes on one hostname (redirect_uri is this origin's callback), and a domain-wide
62
+ // flow cookie would only let two surfaces' concurrent logins clobber each other.
63
+ const domain = resolveCookieDomain(cfg.cookieDomain, url.hostname);
64
+ if (url.pathname === '/api/auth/login') {
65
+ const screen = url.searchParams.get('screen_hint');
66
+ const { location, flow } = await beginLogin(env, origin, {
67
+ returnTo: safePath(url.searchParams.get('returnTo')),
68
+ loginHint: url.searchParams.get('login_hint') || undefined,
69
+ screenHint: screen === 'signup' || screen === 'login' ? screen : undefined,
70
+ });
71
+ return redirectWith(location, [cookie(FLOW_COOKIE, flow, origin, FLOW_MAXAGE)]);
72
+ }
73
+ if (url.pathname === '/api/auth/callback') {
74
+ const flow = readCookie(request.headers.get('cookie'), FLOW_COOKIE);
75
+ const clearFlow = cookie(FLOW_COOKIE, '', origin, 0);
76
+ try {
77
+ const { session, returnTo } = await completeLogin(env, origin, url, flow);
78
+ return redirectWith(safePath(returnTo) ?? '/', [
79
+ clearFlow,
80
+ ...sessionCookies(session, origin, SESSION_MAXAGE, domain),
81
+ ]);
82
+ }
83
+ catch (err) {
84
+ // Loud in the logs, opaque to the browser — same stance as the oidc-rp mount.
85
+ console.error('oidc.callback.failed', { reason: err instanceof Error ? err.message : String(err) });
86
+ return redirectWith('/?error=auth', [clearFlow]);
87
+ }
88
+ }
89
+ if (url.pathname === '/api/auth/logout') {
90
+ return redirectWith(safePath(url.searchParams.get('returnTo')) ?? '/', [
91
+ ...sessionCookies('', origin, 0, domain),
92
+ ]);
93
+ }
94
+ // Anything else under /api/auth/* (sign-up, password endpoints, …) has no server
95
+ // here — accounts live at the issuer. JSON, never a fall-through to the SPA.
96
+ return Response.json({ error: 'this instance authenticates at its OIDC issuer', issuer: cfg.issuer }, { status: 404 });
97
+ }
98
+ async function resolve(headers) {
99
+ const session = await verifySession(env, readCookie(headers.get('cookie'), SESSION_COOKIE));
100
+ if (session)
101
+ return { sub: session.id, email: session.email ?? null, name: session.name ?? null };
102
+ // No cookie session — an API client presenting the issuer's own token directly.
103
+ return bearerVerifier(cfg).resolve(headers);
104
+ }
105
+ return { handle, resolve };
106
+ }
107
+ //# sourceMappingURL=oidc-rp-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-rp-provider.js","sourceRoot":"","sources":["../src/oidc-rp-provider.ts"],"names":[],"mappings":"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"}
package/dist/oidc.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { type JWTVerifyGetKey } from 'jose';
2
+ import type { AuthProvider } from './provider.js';
3
+ /**
4
+ * Standard OIDC as an `AuthProvider` — token-based, so it covers Supabase (now an OIDC
5
+ * issuer), Auth0, AuthHero, Keycloak, Zitadel, … all the same way. Login happens at the
6
+ * issuer (the SPA redirects / uses the issuer's client and gets a token); the app just
7
+ * VERIFIES the presented JWT against the issuer's JWKS and reads the subject. There are no
8
+ * server-side auth endpoints to run, so `handle` is informational.
9
+ *
10
+ * workerd-safe: `jose` + Web Crypto only (the same stack `@substrat-run/oidc-rp` uses).
11
+ */
12
+ export interface OidcConfig {
13
+ /** The issuer URL (`iss`) — its `/.well-known/openid-configuration` gives the JWKS. */
14
+ issuer: string;
15
+ /** Expected audience (`aud`), if the issuer sets one for this app. */
16
+ audience?: string;
17
+ /** Override the JWKS URI (skip discovery) — e.g. a self-hosted issuer. */
18
+ jwksUri?: string;
19
+ /** Inject the key resolver directly — tests / a static JWKS. Defaults to the issuer's remote JWKS. */
20
+ keys?: JWTVerifyGetKey;
21
+ }
22
+ export declare function oidcAuthProvider(cfg: OidcConfig): AuthProvider;
23
+ //# sourceMappingURL=oidc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,eAAe,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sGAAsG;IACtG,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AA0BD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,YAAY,CAiD9D"}
package/dist/oidc.js ADDED
@@ -0,0 +1,77 @@
1
+ import { jwtVerify, createRemoteJWKSet } from 'jose';
2
+ /** Discover the JWKS URI from the issuer's OIDC metadata, cached per issuer for the isolate. */
3
+ const discoveryCache = new Map();
4
+ function discoverJwksUri(issuer) {
5
+ let p = discoveryCache.get(issuer);
6
+ if (!p) {
7
+ p = (async () => {
8
+ const url = `${issuer.replace(/\/$/, '')}/.well-known/openid-configuration`;
9
+ const res = await fetch(url);
10
+ if (!res.ok)
11
+ throw new Error(`OIDC discovery failed for ${issuer}: ${res.status}`);
12
+ const meta = (await res.json());
13
+ if (!meta.jwks_uri)
14
+ throw new Error(`OIDC discovery for ${issuer} has no jwks_uri`);
15
+ return meta.jwks_uri;
16
+ })();
17
+ discoveryCache.set(issuer, p);
18
+ }
19
+ return p;
20
+ }
21
+ function bearerFrom(headers) {
22
+ const auth = headers.get('authorization');
23
+ if (auth?.toLowerCase().startsWith('bearer '))
24
+ return auth.slice(7).trim() || null;
25
+ return null;
26
+ }
27
+ export function oidcAuthProvider(cfg) {
28
+ let keysPromise;
29
+ const getKeys = async () => {
30
+ if (cfg.keys)
31
+ return cfg.keys;
32
+ if (!keysPromise) {
33
+ keysPromise = (async () => {
34
+ const jwksUri = cfg.jwksUri ?? (await discoverJwksUri(cfg.issuer));
35
+ return createRemoteJWKSet(new URL(jwksUri));
36
+ })();
37
+ }
38
+ return keysPromise;
39
+ };
40
+ async function verify(token) {
41
+ try {
42
+ const { payload } = await jwtVerify(token, await getKeys(), {
43
+ issuer: cfg.issuer,
44
+ ...(cfg.audience ? { audience: cfg.audience } : {}),
45
+ });
46
+ if (!payload.sub)
47
+ return null;
48
+ const meta = payload;
49
+ return {
50
+ sub: String(payload.sub),
51
+ email: typeof meta['email'] === 'string' ? meta['email'] : null,
52
+ name: typeof meta['name'] === 'string'
53
+ ? meta['name']
54
+ : typeof meta['user_metadata']?.name === 'string'
55
+ ? (meta['user_metadata'].name)
56
+ : null,
57
+ };
58
+ }
59
+ catch {
60
+ return null; // bad signature / expired / wrong issuer — resolve to nobody, fail closed
61
+ }
62
+ }
63
+ return {
64
+ async handle() {
65
+ return Response.json({
66
+ provider: 'oidc',
67
+ issuer: cfg.issuer,
68
+ note: 'authenticate at the issuer, then present the token as `Authorization: Bearer`',
69
+ });
70
+ },
71
+ async resolve(headers) {
72
+ const token = bearerFrom(headers);
73
+ return token ? verify(token) : null;
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The `AuthProvider` contract — the ONLY thing a vertical's app depends on for identity.
3
+ *
4
+ * The application codes to this interface and does not care WHICH auth is behind it: Better
5
+ * Auth running in a per-tenant Durable Object (`identity-do.ts`), an OIDC issuer (`oidc.ts`),
6
+ * or a test mock all satisfy it. Swapping the implementation never touches the app.
7
+ *
8
+ * - `handle` — the credential/session endpoints (`/api/auth/*`): sign-up, login, logout,
9
+ * callbacks. The worker forwards the raw request; the provider owns what's behind it.
10
+ * - `resolve` — turn the current request into a verified subject, or null. `sub` is the
11
+ * provider's stable subject id; mapping it to a Substrat `PrincipalId` is a SEPARATE,
12
+ * per-scope concern (the identity directory), not the provider's job.
13
+ */
14
+ export interface AuthSubject {
15
+ /** The provider's stable subject id — a Better Auth user id, an OIDC `sub`, … */
16
+ sub: string;
17
+ email: string | null;
18
+ name: string | null;
19
+ }
20
+ export interface AuthProvider {
21
+ handle(request: Request): Promise<Response>;
22
+ resolve(headers: Headers): Promise<AuthSubject | null>;
23
+ }
24
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACxD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The per-tenant SITE REGISTRY (M2 of multi-scope-manyfold.md) — the vertical's own memory of
3
+ * its sites (each site IS a scope), so it can list and switch between them CP-lessly. Kept as
4
+ * plain functions over a minimal SQLite `exec` seam rather than methods on the IdentityDO, so
5
+ * the logic is unit-testable against a plain SQLite without standing up a Durable Object (the
6
+ * DO module pulls in Better Auth, which the workers test pool cannot load). The IdentityDO's
7
+ * `recordSite`/`listSites`/`resolveSiteScope` delegate here.
8
+ */
9
+ /**
10
+ * The slice of `SqlStorage` the registry uses: `exec(query, ...params)` returning an iterable
11
+ * of row objects. Both the DO's `ctx.storage.sql` and a plain SQLite driver satisfy it.
12
+ */
13
+ export interface RegistrySql {
14
+ exec(query: string, ...params: unknown[]): Iterable<Record<string, unknown>>;
15
+ }
16
+ /** The `site` table (+ slug index), folded into the IdentityDO's schema statements. */
17
+ export declare const SITE_REGISTRY_DDL: readonly string[];
18
+ export interface SiteRow {
19
+ scopeId: string;
20
+ slug: string;
21
+ name: string;
22
+ }
23
+ /**
24
+ * Record a site at provision. Idempotent: a re-provision (K-31) updates the slug/name in place
25
+ * but keeps `created_at`, so the switcher's ordering stays stable.
26
+ */
27
+ export declare function recordSite(sql: RegistrySql, scopeId: string, slug: string, name: string): void;
28
+ /** The tenant's sites, oldest first — the in-app site switcher's list. */
29
+ export declare function listSites(sql: RegistrySql): SiteRow[];
30
+ /** Drop a site from the registry — the vertical's half of archiving a scope (it no longer lists or
31
+ * resolves to the archived site). Idempotent. The platform archives the scope directory-side. */
32
+ export declare function forgetSite(sql: RegistrySql, scopeId: string): void;
33
+ /**
34
+ * Resolve a site SLUG to its scope id — how the worker turns the app's `x-site` selection into
35
+ * the scope to open. Null ⇒ no such site. Tenant-scoped by construction: the registry lives in
36
+ * a per-tenant DO, so a slug can only resolve to a scope of that tenant.
37
+ */
38
+ export declare function resolveSiteScope(sql: RegistrySql, slug: string): string | null;
39
+ //# sourceMappingURL=site-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-registry.d.ts","sourceRoot":"","sources":["../src/site-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9E;AAED,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAI9C,CAAC;AAEF,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ9F;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,EAAE,CAMrD;AAED;kGACkG;AAClG,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK9E"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The per-tenant SITE REGISTRY (M2 of multi-scope-manyfold.md) — the vertical's own memory of
3
+ * its sites (each site IS a scope), so it can list and switch between them CP-lessly. Kept as
4
+ * plain functions over a minimal SQLite `exec` seam rather than methods on the IdentityDO, so
5
+ * the logic is unit-testable against a plain SQLite without standing up a Durable Object (the
6
+ * DO module pulls in Better Auth, which the workers test pool cannot load). The IdentityDO's
7
+ * `recordSite`/`listSites`/`resolveSiteScope` delegate here.
8
+ */
9
+ /** The `site` table (+ slug index), folded into the IdentityDO's schema statements. */
10
+ export const SITE_REGISTRY_DDL = [
11
+ `CREATE TABLE IF NOT EXISTS site (scope_id TEXT PRIMARY KEY, slug TEXT NOT NULL, name TEXT NOT NULL,
12
+ created_at INTEGER NOT NULL DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)))`,
13
+ `CREATE INDEX IF NOT EXISTS site_by_slug ON site (slug)`,
14
+ ];
15
+ /**
16
+ * Record a site at provision. Idempotent: a re-provision (K-31) updates the slug/name in place
17
+ * but keeps `created_at`, so the switcher's ordering stays stable.
18
+ */
19
+ export function recordSite(sql, scopeId, slug, name) {
20
+ sql.exec(`INSERT INTO site (scope_id, slug, name) VALUES (?, ?, ?)
21
+ ON CONFLICT(scope_id) DO UPDATE SET slug = excluded.slug, name = excluded.name`, scopeId, slug, name);
22
+ }
23
+ /** The tenant's sites, oldest first — the in-app site switcher's list. */
24
+ export function listSites(sql) {
25
+ return [...sql.exec('SELECT scope_id, slug, name FROM site ORDER BY created_at')].map((r) => ({
26
+ scopeId: r.scope_id,
27
+ slug: r.slug,
28
+ name: r.name,
29
+ }));
30
+ }
31
+ /** Drop a site from the registry — the vertical's half of archiving a scope (it no longer lists or
32
+ * resolves to the archived site). Idempotent. The platform archives the scope directory-side. */
33
+ export function forgetSite(sql, scopeId) {
34
+ sql.exec('DELETE FROM site WHERE scope_id = ?', scopeId);
35
+ }
36
+ /**
37
+ * Resolve a site SLUG to its scope id — how the worker turns the app's `x-site` selection into
38
+ * the scope to open. Null ⇒ no such site. Tenant-scoped by construction: the registry lives in
39
+ * a per-tenant DO, so a slug can only resolve to a scope of that tenant.
40
+ */
41
+ export function resolveSiteScope(sql, slug) {
42
+ const row = [...sql.exec('SELECT scope_id FROM site WHERE slug = ?', slug)][0];
43
+ return row ? row.scope_id : null;
44
+ }
45
+ //# sourceMappingURL=site-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-registry.js","sourceRoot":"","sources":["../src/site-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD;0FACwF;IACxF,wDAAwD;CACzD,CAAC;AAQF;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAgB,EAAE,OAAe,EAAE,IAAY,EAAE,IAAY;IACtF,GAAG,CAAC,IAAI,CACN;oFACgF,EAChF,OAAO,EACP,IAAI,EACJ,IAAI,CACL,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,GAAgB;IACxC,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,OAAO,EAAE,CAAC,CAAC,QAAkB;QAC7B,IAAI,EAAE,CAAC,CAAC,IAAc;QACtB,IAAI,EAAE,CAAC,CAAC,IAAc;KACvB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;kGACkG;AAClG,MAAM,UAAU,UAAU,CAAC,GAAgB,EAAE,OAAe;IAC1D,GAAG,CAAC,IAAI,CAAC,qCAAqC,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAgB,EAAE,IAAY;IAC7D,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,0CAA0C,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAEhE,CAAC;IACd,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@substrat-run/vertical-auth",
3
+ "version": "0.6.0",
4
+ "description": "Pluggable auth for Substrat verticals — the AuthProvider contract, an OIDC provider (Supabase/Auth0/AuthHero/Keycloak), a full relying-party login flow, and a per-tenant identity Durable Object (Better Auth + sub→principal directory). Workers-targeted; the vertical worker bundles it.",
5
+ "license": "AGPL-3.0-only",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./provider": {
15
+ "types": "./dist/provider.d.ts",
16
+ "default": "./dist/provider.js"
17
+ },
18
+ "./oidc": {
19
+ "types": "./dist/oidc.d.ts",
20
+ "default": "./dist/oidc.js"
21
+ },
22
+ "./oidc-rp-provider": {
23
+ "types": "./dist/oidc-rp-provider.d.ts",
24
+ "default": "./dist/oidc-rp-provider.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.json",
32
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
33
+ "test": "vitest run"
34
+ },
35
+ "dependencies": {
36
+ "@substrat-run/oidc-rp": "workspace:^",
37
+ "@substrat-run/psl": "workspace:^",
38
+ "better-auth": "^1.6.23",
39
+ "drizzle-orm": "^0.45.2",
40
+ "jose": "^6.2.3"
41
+ },
42
+ "devDependencies": {
43
+ "@cloudflare/workers-types": "^4.20250109.0",
44
+ "@types/better-sqlite3": "^9.6.0",
45
+ "better-sqlite3": "^13.0.3",
46
+ "typescript": "^7.0.0",
47
+ "vitest": "^3.0.0"
48
+ }
49
+ }