@authhero/cloudflare-adapter 2.34.1 → 2.35.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/dist/cloudflare-adapter.cjs +35 -99
- package/dist/cloudflare-adapter.d.ts +140 -80
- package/dist/cloudflare-adapter.mjs +367 -11670
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/wfp/dispatch-sync-defaults.d.ts +33 -0
- package/dist/types/wfp/index.d.ts +8 -0
- package/dist/types/wfp/tenant-app.d.ts +62 -0
- package/dist/types/wfp-provisioner/cf-api.d.ts +17 -5
- package/dist/types/wfp-provisioner/index.d.ts +2 -0
- package/dist/types/wfp-provisioner/types.d.ts +12 -0
- package/dist/types/wfp-provisioner/wfp-forward.d.ts +37 -0
- package/dist/wfp.cjs +1 -0
- package/dist/wfp.d.ts +120 -0
- package/dist/wfp.mjs +64 -0
- package/package.json +19 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DataAdapters } from "@authhero/adapter-interfaces";
|
|
2
|
+
import { type DefaultsPayloadEntities } from "@authhero/multi-tenancy";
|
|
3
|
+
import type { DispatchNamespace } from "../code-executor";
|
|
4
|
+
export interface DispatchSyncDefaultsOptions {
|
|
5
|
+
/** The dispatch namespace binding the tenant workers live in. */
|
|
6
|
+
dispatcher: DispatchNamespace;
|
|
7
|
+
/** Script-name convention. Supports `{tenant_id}`. */
|
|
8
|
+
scriptNameTemplate?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Shared secret the tenant worker checks on `/internal/sync-defaults`
|
|
11
|
+
* (`WFP_INTERNAL_SYNC_SECRET`). Sent as a bearer token.
|
|
12
|
+
*/
|
|
13
|
+
internalSecret: string;
|
|
14
|
+
/** The control-plane tenant id whose defaults are projected. */
|
|
15
|
+
controlPlaneTenantId: string;
|
|
16
|
+
/** Adapters for reading the control plane's rows (secrets decrypted). */
|
|
17
|
+
controlPlaneAdapters: DataAdapters;
|
|
18
|
+
/** Which entities to include in the payload. Defaults to all. */
|
|
19
|
+
entities?: DefaultsPayloadEntities;
|
|
20
|
+
/** Per-push timeout. Defaults to 30s. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds a **push** function that projects the control plane's defaults into a
|
|
25
|
+
* single tenant's database over a dispatch namespace.
|
|
26
|
+
*
|
|
27
|
+
* Pure push: the control plane builds the payload with
|
|
28
|
+
* `buildControlPlaneDefaultsPayload` and POSTs it to the tenant worker's
|
|
29
|
+
* `/internal/sync-defaults` route (which applies it). The tenant worker never
|
|
30
|
+
* calls back to the control plane. Use the returned function as the
|
|
31
|
+
* provision-time seed and for on-change / rotation re-syncs.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createDispatchSyncDefaults(options: DispatchSyncDefaultsOptions): (tenantId: string) => Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@authhero/cloudflare-adapter/wfp` — the Workers-for-Platforms control-plane
|
|
3
|
+
* sync surface. Kept out of the package's main entry because it imports the
|
|
4
|
+
* app-level packages (`authhero`, `@authhero/multi-tenancy`), which are
|
|
5
|
+
* **optional peer dependencies**: install them only if you import this subpath.
|
|
6
|
+
*/
|
|
7
|
+
export { createDispatchSyncDefaults, type DispatchSyncDefaultsOptions, } from "./dispatch-sync-defaults";
|
|
8
|
+
export { createWfpTenantApp, type WfpTenantEnv, type WfpTenantAppOptions, } from "./tenant-app";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type ExecutionContext } from "hono";
|
|
2
|
+
import { type AuthHeroConfig, type DataAdapters } from "authhero";
|
|
3
|
+
/**
|
|
4
|
+
* Env contract for a WFP tenant worker — the bindings + secrets
|
|
5
|
+
* `createCloudflareWfpD1Provisioner` (and the host's `secrets` resolver) set on
|
|
6
|
+
* each tenant script. `AUTH_DB` is the D1 binding; the rest are plain
|
|
7
|
+
* text / secret text.
|
|
8
|
+
*/
|
|
9
|
+
export interface WfpTenantEnv {
|
|
10
|
+
/** Per-tenant D1 binding. Consumed by the host's `createDataAdapter`. */
|
|
11
|
+
AUTH_DB: unknown;
|
|
12
|
+
/** This tenant's own at-rest encryption key (base64, 32 bytes). */
|
|
13
|
+
ENCRYPTION_KEY: string;
|
|
14
|
+
/** This tenant's JWT `iss`. */
|
|
15
|
+
ISSUER: string;
|
|
16
|
+
/** Control-plane tenant id whose projected defaults this worker inherits. */
|
|
17
|
+
CONTROL_PLANE_TENANT_ID: string;
|
|
18
|
+
/**
|
|
19
|
+
* The control plane's own issuer. Accepted in addition to `ISSUER` so a
|
|
20
|
+
* control-plane-minted admin token forwarded here verifies — its signature is
|
|
21
|
+
* checked against the control plane's PUBLIC keys, which are projected into
|
|
22
|
+
* this tenant's DB (no runtime JWKS fetch).
|
|
23
|
+
*/
|
|
24
|
+
CONTROL_PLANE_ISSUER: string;
|
|
25
|
+
/**
|
|
26
|
+
* Key for the `cp` key-ring id — encrypts inherited control-plane rows so the
|
|
27
|
+
* tenant operator can hold but not read them. When omitted, a single key is
|
|
28
|
+
* used for everything.
|
|
29
|
+
*/
|
|
30
|
+
CONTROL_PLANE_ENCRYPTION_KEY?: string;
|
|
31
|
+
/** Shared secret authenticating control-plane pushes to `/internal/sync-defaults`. */
|
|
32
|
+
WFP_INTERNAL_SYNC_SECRET?: string;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface WfpTenantAppOptions<Env extends WfpTenantEnv = WfpTenantEnv> {
|
|
36
|
+
/**
|
|
37
|
+
* Builds the tenant's **base** data adapters from its env (typically
|
|
38
|
+
* `createAdapters(drizzle(env.AUTH_DB))`). Injected so this package carries no
|
|
39
|
+
* ORM dependency — the host owns the `@authhero/drizzle` (or other) import.
|
|
40
|
+
*/
|
|
41
|
+
createDataAdapter: (env: Env) => DataAdapters;
|
|
42
|
+
/**
|
|
43
|
+
* Hook to extend or override the authhero config before `init` — add custom
|
|
44
|
+
* hooks, code executors, `signingKeyMode`, extra issuers, etc. Receives the
|
|
45
|
+
* scaffold's base config and the env.
|
|
46
|
+
*/
|
|
47
|
+
configure?: (base: AuthHeroConfig, env: Env) => AuthHeroConfig;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Builds a WFP tenant worker `{ fetch }` handler: key-ring encryption over the
|
|
51
|
+
* tenant's own D1, runtime fallback for inherited control-plane defaults, the
|
|
52
|
+
* `/internal/sync-defaults` push receiver, and the control-plane issuer gate —
|
|
53
|
+
* all from one factory.
|
|
54
|
+
*
|
|
55
|
+
* Pure push: this worker never calls the control plane at request time. Defaults
|
|
56
|
+
* and the control plane's public verify keys arrive only via
|
|
57
|
+
* `/internal/sync-defaults`. The built app is cached per `env` (one build per
|
|
58
|
+
* isolate).
|
|
59
|
+
*/
|
|
60
|
+
export declare function createWfpTenantApp<Env extends WfpTenantEnv = WfpTenantEnv>(options: WfpTenantAppOptions<Env>): {
|
|
61
|
+
fetch: (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
|
|
62
|
+
};
|
|
@@ -33,12 +33,24 @@ export interface D1QueryResult {
|
|
|
33
33
|
meta?: Record<string, unknown>;
|
|
34
34
|
results?: unknown[];
|
|
35
35
|
}
|
|
36
|
-
export
|
|
37
|
-
type: "d1"
|
|
36
|
+
export type ScriptBinding = {
|
|
37
|
+
type: "d1";
|
|
38
38
|
name: string;
|
|
39
|
-
id
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
id: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: "plain_text";
|
|
42
|
+
name: string;
|
|
43
|
+
text: string;
|
|
44
|
+
} | {
|
|
45
|
+
type: "secret_text";
|
|
46
|
+
name: string;
|
|
47
|
+
text: string;
|
|
48
|
+
} | {
|
|
49
|
+
type: "service";
|
|
50
|
+
name: string;
|
|
51
|
+
service: string;
|
|
52
|
+
environment?: string;
|
|
53
|
+
};
|
|
42
54
|
export interface ScriptUploadOptions {
|
|
43
55
|
/** Script source (JavaScript ES module). */
|
|
44
56
|
script: string;
|
|
@@ -2,5 +2,7 @@ export { createCloudflareWfpD1Provisioner } from "./provisioner";
|
|
|
2
2
|
export type { CloudflareWfpD1Provisioner, CloudflareWfpD1ProvisionerOptions, ProvisionResult, ProvisionerMigration, TenantSecretsResolver, } from "./types";
|
|
3
3
|
export { createWfpTenantProvisioningHook } from "./tenant-hook";
|
|
4
4
|
export type { WfpTenantProvisioningHook, WfpTenantProvisioningHookOptions, } from "./tenant-hook";
|
|
5
|
+
export { createWfpForwardMiddleware } from "./wfp-forward";
|
|
6
|
+
export type { WfpForwardOptions } from "./wfp-forward";
|
|
5
7
|
export { CloudflareApiClient, CloudflareApiError } from "./cf-api";
|
|
6
8
|
export type { CfApiClientOptions, D1Database, D1QueryResult, ScriptBinding, ScriptUploadOptions, } from "./cf-api";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ScriptBinding } from "./cf-api";
|
|
1
2
|
/**
|
|
2
3
|
* Public types for the Workers-for-Platforms + D1 tenant provisioner.
|
|
3
4
|
*
|
|
@@ -90,6 +91,17 @@ export interface CloudflareWfpD1ProvisionerOptions {
|
|
|
90
91
|
* the per-script secrets API.
|
|
91
92
|
*/
|
|
92
93
|
secrets: TenantSecretsResolver;
|
|
94
|
+
/**
|
|
95
|
+
* Extra bindings to attach to every provisioned tenant worker, appended
|
|
96
|
+
* after the always-present `AUTH_DB` (d1) and `CONTROL_PLANE_BASE_URL`
|
|
97
|
+
* (plain_text) bindings. Use this to wire e.g. a `service` binding to a
|
|
98
|
+
* shared upstream worker, or additional `plain_text` config the tenant
|
|
99
|
+
* bundle expects. Secrets still go through the `secrets` resolver, not here.
|
|
100
|
+
*
|
|
101
|
+
* `uploadNamespacedScript` forwards these verbatim into the CF script
|
|
102
|
+
* metadata, so any binding type the CF API accepts is valid.
|
|
103
|
+
*/
|
|
104
|
+
extraBindings?: ScriptBinding[];
|
|
93
105
|
/**
|
|
94
106
|
* Naming convention for the namespaced script. Supports `{tenant_id}`
|
|
95
107
|
* placeholder. Defaults to `"{tenant_id}"`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Context, MiddlewareHandler } from "hono";
|
|
2
|
+
import type { TenantsDataAdapter } from "@authhero/adapter-interfaces";
|
|
3
|
+
export interface WfpForwardOptions {
|
|
4
|
+
/** Tenants adapter, used to look up the resolved tenant's `deployment_type`. */
|
|
5
|
+
tenants: TenantsDataAdapter;
|
|
6
|
+
/**
|
|
7
|
+
* The control-plane tenant id. Requests for this tenant (and anything not a
|
|
8
|
+
* `wfp` tenant) fall through to the local app instead of being dispatched.
|
|
9
|
+
*/
|
|
10
|
+
controlPlaneTenantId: string;
|
|
11
|
+
/** Env binding name of the dispatch namespace. Defaults to `"DISPATCHER"`. */
|
|
12
|
+
dispatcherBinding?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Script-name convention for a tenant's worker. Supports `{tenant_id}`.
|
|
15
|
+
* Defaults to `"tenant-{tenant_id}-auth"` — must match the provisioner's
|
|
16
|
+
* `scriptNameTemplate`.
|
|
17
|
+
*/
|
|
18
|
+
scriptNameTemplate?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the tenant id for the incoming request. Defaults to reading the
|
|
21
|
+
* `tenant-id` header (legacy compatibility). Replace it to resolve from a
|
|
22
|
+
* subdomain or custom domain. Return `undefined` to fall through locally.
|
|
23
|
+
*/
|
|
24
|
+
resolveTenantId?: (c: Context) => string | undefined | Promise<string | undefined>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Hono middleware that forwards a request to its tenant's WFP worker over a
|
|
28
|
+
* dispatch namespace, instead of serving it from the current (control-plane)
|
|
29
|
+
* worker.
|
|
30
|
+
*
|
|
31
|
+
* For each request it resolves a tenant id, and **only** dispatches when that
|
|
32
|
+
* tenant exists and has `deployment_type === "wfp"`. The control-plane tenant,
|
|
33
|
+
* unknown tenants, and shared (colocated) tenants all fall through to the next
|
|
34
|
+
* handler so the local app serves them. The tenant worker receives the original
|
|
35
|
+
* request verbatim and owns the full response.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createWfpForwardMiddleware(options: WfpForwardOptions): MiddlewareHandler;
|
package/dist/wfp.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@authhero/multi-tenancy"),t=require("hono"),n=require("authhero");var r=`tenant-{tenant_id}-auth`,i=3e4,a=`/internal/sync-defaults`;function o(e,t){return e.replace(/\{tenant_id\}/g,t)}function s(t){let{dispatcher:n,scriptNameTemplate:s=r,internalSecret:c,controlPlaneTenantId:l,controlPlaneAdapters:u,entities:d,timeoutMs:f=i}=t;return async t=>{let r=await(0,e.buildControlPlaneDefaultsPayload)(u,l,d),i=o(s,t),p=new AbortController,m=setTimeout(()=>p.abort(),f),h;try{h=await n.get(i).fetch(`https://tenant.internal${a}`,{method:`POST`,headers:{"content-type":`application/json`,authorization:`Bearer ${c}`},body:JSON.stringify(r),signal:p.signal})}finally{clearTimeout(m)}if(!h.ok){let e=await h.text().catch(()=>``);throw Error(`sync-defaults push to "${i}" failed: ${h.status} ${e.slice(0,256)}`)}}}var c=`cp`,l=`/internal/sync-defaults`;async function u(r,i){let a=r.CONTROL_PLANE_TENANT_ID,o=await(0,n.loadEncryptionKey)(r.ENCRYPTION_KEY),s=r.CONTROL_PLANE_ENCRYPTION_KEY?{default:o,keys:{[c]:await(0,n.loadEncryptionKey)(r.CONTROL_PLANE_ENCRYPTION_KEY)}}:{default:o},u=(0,n.createEncryptedDataAdapterWithKeyRing)(i.createDataAdapter(r),s,{resolveEncryptKeyId:e=>e===a?c:void 0}),d={dataAdapter:(0,e.withRuntimeFallback)(u,{controlPlaneTenantId:a}),additionalIssuers:()=>[r.CONTROL_PLANE_ISSUER]},{app:f}=(0,n.init)(i.configure?i.configure(d,r):d),p=new t.Hono;return p.post(l,async t=>{let n=r.WFP_INTERNAL_SYNC_SECRET,i=t.req.header(`authorization`);if(!n||i!==`Bearer ${n}`)return t.json({error:`unauthorized`},401);let o;try{o=await t.req.json()}catch{return t.json({error:`invalid JSON`},400)}let s=await(0,e.applyControlPlaneDefaultsPayload)(o,u,a);return t.json(s)}),p.route(`/`,f),p}function d(e){let t=new WeakMap;return{fetch(n,r,i){let a=t.get(r);return a||(a=u(r,e),t.set(r,a)),a.then(e=>e.fetch(n,r,i))}}}exports.createDispatchSyncDefaults=s,exports.createWfpTenantApp=d;
|
package/dist/wfp.d.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { DataAdapters } from '@authhero/adapter-interfaces';
|
|
2
|
+
import { DefaultsPayloadEntities } from '@authhero/multi-tenancy';
|
|
3
|
+
import { ExecutionContext } from 'hono';
|
|
4
|
+
import { DataAdapters as DataAdapters$1, AuthHeroConfig } from 'authhero';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cloudflare Workers for Platforms dispatch namespace binding type.
|
|
8
|
+
* This is the type of `env.DISPATCHER` when configured in wrangler.toml:
|
|
9
|
+
*
|
|
10
|
+
* ```toml
|
|
11
|
+
* [[dispatch_namespaces]]
|
|
12
|
+
* binding = "DISPATCHER"
|
|
13
|
+
* namespace = "authhero-hooks"
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
interface DispatchNamespace {
|
|
17
|
+
get(name: string, options?: Record<string, unknown>, init?: {
|
|
18
|
+
limits?: {
|
|
19
|
+
cpuMs?: number;
|
|
20
|
+
subrequests?: number;
|
|
21
|
+
};
|
|
22
|
+
}): {
|
|
23
|
+
fetch(request: Request | string, init?: RequestInit): Promise<Response>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DispatchSyncDefaultsOptions {
|
|
28
|
+
/** The dispatch namespace binding the tenant workers live in. */
|
|
29
|
+
dispatcher: DispatchNamespace;
|
|
30
|
+
/** Script-name convention. Supports `{tenant_id}`. */
|
|
31
|
+
scriptNameTemplate?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Shared secret the tenant worker checks on `/internal/sync-defaults`
|
|
34
|
+
* (`WFP_INTERNAL_SYNC_SECRET`). Sent as a bearer token.
|
|
35
|
+
*/
|
|
36
|
+
internalSecret: string;
|
|
37
|
+
/** The control-plane tenant id whose defaults are projected. */
|
|
38
|
+
controlPlaneTenantId: string;
|
|
39
|
+
/** Adapters for reading the control plane's rows (secrets decrypted). */
|
|
40
|
+
controlPlaneAdapters: DataAdapters;
|
|
41
|
+
/** Which entities to include in the payload. Defaults to all. */
|
|
42
|
+
entities?: DefaultsPayloadEntities;
|
|
43
|
+
/** Per-push timeout. Defaults to 30s. */
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds a **push** function that projects the control plane's defaults into a
|
|
48
|
+
* single tenant's database over a dispatch namespace.
|
|
49
|
+
*
|
|
50
|
+
* Pure push: the control plane builds the payload with
|
|
51
|
+
* `buildControlPlaneDefaultsPayload` and POSTs it to the tenant worker's
|
|
52
|
+
* `/internal/sync-defaults` route (which applies it). The tenant worker never
|
|
53
|
+
* calls back to the control plane. Use the returned function as the
|
|
54
|
+
* provision-time seed and for on-change / rotation re-syncs.
|
|
55
|
+
*/
|
|
56
|
+
declare function createDispatchSyncDefaults(options: DispatchSyncDefaultsOptions): (tenantId: string) => Promise<void>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Env contract for a WFP tenant worker — the bindings + secrets
|
|
60
|
+
* `createCloudflareWfpD1Provisioner` (and the host's `secrets` resolver) set on
|
|
61
|
+
* each tenant script. `AUTH_DB` is the D1 binding; the rest are plain
|
|
62
|
+
* text / secret text.
|
|
63
|
+
*/
|
|
64
|
+
interface WfpTenantEnv {
|
|
65
|
+
/** Per-tenant D1 binding. Consumed by the host's `createDataAdapter`. */
|
|
66
|
+
AUTH_DB: unknown;
|
|
67
|
+
/** This tenant's own at-rest encryption key (base64, 32 bytes). */
|
|
68
|
+
ENCRYPTION_KEY: string;
|
|
69
|
+
/** This tenant's JWT `iss`. */
|
|
70
|
+
ISSUER: string;
|
|
71
|
+
/** Control-plane tenant id whose projected defaults this worker inherits. */
|
|
72
|
+
CONTROL_PLANE_TENANT_ID: string;
|
|
73
|
+
/**
|
|
74
|
+
* The control plane's own issuer. Accepted in addition to `ISSUER` so a
|
|
75
|
+
* control-plane-minted admin token forwarded here verifies — its signature is
|
|
76
|
+
* checked against the control plane's PUBLIC keys, which are projected into
|
|
77
|
+
* this tenant's DB (no runtime JWKS fetch).
|
|
78
|
+
*/
|
|
79
|
+
CONTROL_PLANE_ISSUER: string;
|
|
80
|
+
/**
|
|
81
|
+
* Key for the `cp` key-ring id — encrypts inherited control-plane rows so the
|
|
82
|
+
* tenant operator can hold but not read them. When omitted, a single key is
|
|
83
|
+
* used for everything.
|
|
84
|
+
*/
|
|
85
|
+
CONTROL_PLANE_ENCRYPTION_KEY?: string;
|
|
86
|
+
/** Shared secret authenticating control-plane pushes to `/internal/sync-defaults`. */
|
|
87
|
+
WFP_INTERNAL_SYNC_SECRET?: string;
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
}
|
|
90
|
+
interface WfpTenantAppOptions<Env extends WfpTenantEnv = WfpTenantEnv> {
|
|
91
|
+
/**
|
|
92
|
+
* Builds the tenant's **base** data adapters from its env (typically
|
|
93
|
+
* `createAdapters(drizzle(env.AUTH_DB))`). Injected so this package carries no
|
|
94
|
+
* ORM dependency — the host owns the `@authhero/drizzle` (or other) import.
|
|
95
|
+
*/
|
|
96
|
+
createDataAdapter: (env: Env) => DataAdapters$1;
|
|
97
|
+
/**
|
|
98
|
+
* Hook to extend or override the authhero config before `init` — add custom
|
|
99
|
+
* hooks, code executors, `signingKeyMode`, extra issuers, etc. Receives the
|
|
100
|
+
* scaffold's base config and the env.
|
|
101
|
+
*/
|
|
102
|
+
configure?: (base: AuthHeroConfig, env: Env) => AuthHeroConfig;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Builds a WFP tenant worker `{ fetch }` handler: key-ring encryption over the
|
|
106
|
+
* tenant's own D1, runtime fallback for inherited control-plane defaults, the
|
|
107
|
+
* `/internal/sync-defaults` push receiver, and the control-plane issuer gate —
|
|
108
|
+
* all from one factory.
|
|
109
|
+
*
|
|
110
|
+
* Pure push: this worker never calls the control plane at request time. Defaults
|
|
111
|
+
* and the control plane's public verify keys arrive only via
|
|
112
|
+
* `/internal/sync-defaults`. The built app is cached per `env` (one build per
|
|
113
|
+
* isolate).
|
|
114
|
+
*/
|
|
115
|
+
declare function createWfpTenantApp<Env extends WfpTenantEnv = WfpTenantEnv>(options: WfpTenantAppOptions<Env>): {
|
|
116
|
+
fetch: (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { createDispatchSyncDefaults, createWfpTenantApp };
|
|
120
|
+
export type { DispatchSyncDefaultsOptions, WfpTenantAppOptions, WfpTenantEnv };
|
package/dist/wfp.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { applyControlPlaneDefaultsPayload as e, buildControlPlaneDefaultsPayload as t, withRuntimeFallback as n } from "@authhero/multi-tenancy";
|
|
2
|
+
import { Hono as r } from "hono";
|
|
3
|
+
import { createEncryptedDataAdapterWithKeyRing as i, init as a, loadEncryptionKey as o } from "authhero";
|
|
4
|
+
//#region src/wfp/dispatch-sync-defaults.ts
|
|
5
|
+
var s = "tenant-{tenant_id}-auth", c = 3e4, l = "/internal/sync-defaults";
|
|
6
|
+
function u(e, t) {
|
|
7
|
+
return e.replace(/\{tenant_id\}/g, t);
|
|
8
|
+
}
|
|
9
|
+
function d(e) {
|
|
10
|
+
let { dispatcher: n, scriptNameTemplate: r = s, internalSecret: i, controlPlaneTenantId: a, controlPlaneAdapters: o, entities: d, timeoutMs: f = c } = e;
|
|
11
|
+
return async (e) => {
|
|
12
|
+
let s = await t(o, a, d), c = u(r, e), p = new AbortController(), m = setTimeout(() => p.abort(), f), h;
|
|
13
|
+
try {
|
|
14
|
+
h = await n.get(c).fetch(`https://tenant.internal${l}`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: {
|
|
17
|
+
"content-type": "application/json",
|
|
18
|
+
authorization: `Bearer ${i}`
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify(s),
|
|
21
|
+
signal: p.signal
|
|
22
|
+
});
|
|
23
|
+
} finally {
|
|
24
|
+
clearTimeout(m);
|
|
25
|
+
}
|
|
26
|
+
if (!h.ok) {
|
|
27
|
+
let e = await h.text().catch(() => "");
|
|
28
|
+
throw Error(`sync-defaults push to "${c}" failed: ${h.status} ${e.slice(0, 256)}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/wfp/tenant-app.ts
|
|
34
|
+
var f = "cp", p = "/internal/sync-defaults";
|
|
35
|
+
async function m(t, s) {
|
|
36
|
+
let c = t.CONTROL_PLANE_TENANT_ID, l = await o(t.ENCRYPTION_KEY), u = t.CONTROL_PLANE_ENCRYPTION_KEY ? {
|
|
37
|
+
default: l,
|
|
38
|
+
keys: { [f]: await o(t.CONTROL_PLANE_ENCRYPTION_KEY) }
|
|
39
|
+
} : { default: l }, d = i(s.createDataAdapter(t), u, { resolveEncryptKeyId: (e) => e === c ? f : void 0 }), m = {
|
|
40
|
+
dataAdapter: n(d, { controlPlaneTenantId: c }),
|
|
41
|
+
additionalIssuers: () => [t.CONTROL_PLANE_ISSUER]
|
|
42
|
+
}, { app: h } = a(s.configure ? s.configure(m, t) : m), g = new r();
|
|
43
|
+
return g.post(p, async (n) => {
|
|
44
|
+
let r = t.WFP_INTERNAL_SYNC_SECRET, i = n.req.header("authorization");
|
|
45
|
+
if (!r || i !== `Bearer ${r}`) return n.json({ error: "unauthorized" }, 401);
|
|
46
|
+
let a;
|
|
47
|
+
try {
|
|
48
|
+
a = await n.req.json();
|
|
49
|
+
} catch {
|
|
50
|
+
return n.json({ error: "invalid JSON" }, 400);
|
|
51
|
+
}
|
|
52
|
+
let o = await e(a, d, c);
|
|
53
|
+
return n.json(o);
|
|
54
|
+
}), g.route("/", h), g;
|
|
55
|
+
}
|
|
56
|
+
function h(e) {
|
|
57
|
+
let t = /* @__PURE__ */ new WeakMap();
|
|
58
|
+
return { fetch(n, r, i) {
|
|
59
|
+
let a = t.get(r);
|
|
60
|
+
return a || (a = m(r, e), t.set(r, a)), a.then((e) => e.fetch(n, r, i));
|
|
61
|
+
} };
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { d as createDispatchSyncDefaults, h as createWfpTenantApp };
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/markusahlstrand/authhero"
|
|
13
13
|
},
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.35.0",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
"types": "./dist/cloudflare-adapter.d.ts",
|
|
24
24
|
"require": "./dist/cloudflare-adapter.cjs",
|
|
25
25
|
"import": "./dist/cloudflare-adapter.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./wfp": {
|
|
28
|
+
"types": "./dist/wfp.d.ts",
|
|
29
|
+
"require": "./dist/wfp.cjs",
|
|
30
|
+
"import": "./dist/wfp.mjs"
|
|
26
31
|
}
|
|
27
32
|
},
|
|
28
33
|
"devDependencies": {
|
|
@@ -36,12 +41,24 @@
|
|
|
36
41
|
"rollup-plugin-dts": "^6.4.1",
|
|
37
42
|
"typescript": "^5.9.3",
|
|
38
43
|
"vite": "^8.0.14",
|
|
39
|
-
"vitest": "^4.1.7"
|
|
44
|
+
"vitest": "^4.1.7",
|
|
45
|
+
"@authhero/multi-tenancy": "14.25.0",
|
|
46
|
+
"authhero": "8.5.0"
|
|
40
47
|
},
|
|
41
48
|
"peerDependencies": {
|
|
49
|
+
"@authhero/multi-tenancy": "^14.0.0",
|
|
42
50
|
"@hono/zod-openapi": "^1.4.0",
|
|
51
|
+
"authhero": "^3.0.0",
|
|
43
52
|
"hono": "^4.6.8"
|
|
44
53
|
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@authhero/multi-tenancy": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"authhero": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
45
62
|
"dependencies": {
|
|
46
63
|
"nanoid": "^5.1.11",
|
|
47
64
|
"wretch": "^3.0.8",
|