create-authhero 0.43.0 → 0.44.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/proxy/README.md +109 -2
- package/dist/proxy/src/proxy.config.ts +15 -5
- package/package.json +1 -1
package/dist/proxy/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A Cloudflare Worker that proxies incoming requests to upstream services based on the request's `Host` header. Built on [@authhero/proxy](https://www.npmjs.com/package/@authhero/proxy).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This template ships with a static, in-file config so you can run it immediately. For production you'll typically swap that adapter for one that reads routes from your authhero deployment — see [Data sources](#data-sources) below.
|
|
6
|
+
|
|
7
|
+
## Configure your routes (default)
|
|
6
8
|
|
|
7
9
|
Edit [src/proxy.config.ts](src/proxy.config.ts) to map each public hostname to one or more upstream routes. Path patterns support `*` and `:param` segments, and routes are matched in priority order (lower wins).
|
|
8
10
|
|
|
@@ -23,6 +25,111 @@ export const proxyConfig: StaticProxyAdapterOptions = {
|
|
|
23
25
|
};
|
|
24
26
|
```
|
|
25
27
|
|
|
28
|
+
## Data sources
|
|
29
|
+
|
|
30
|
+
The proxy reads its routes through a `ProxyDataAdapter`. Three implementations are common:
|
|
31
|
+
|
|
32
|
+
| Adapter | Best for | Notes |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| **Static** (default) | Local dev, small fixed deployments | Routes baked into the worker bundle; re-deploy to change them. |
|
|
35
|
+
| **Database** | Same-process or co-located deployments | Reads directly from the proxy_routes table that authhero writes to. |
|
|
36
|
+
| **HTTP / management API** | Geographically distributed proxies, hosted Workers | Calls `/api/v2/proxy-routes` on your authhero server with a service token. |
|
|
37
|
+
|
|
38
|
+
The authhero server exposes the management API (`/api/v2/proxy-routes`) and creates the underlying table automatically once the standard adapter migrations have run — see the `local` template's [src/index.ts](../local/src/index.ts).
|
|
39
|
+
|
|
40
|
+
### Database-backed (Kysely)
|
|
41
|
+
|
|
42
|
+
Add the adapter and your Kysely driver, then swap the data line:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install @authhero/kysely-adapter kysely
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Kysely } from "kysely";
|
|
50
|
+
import { createProxyApp } from "@authhero/proxy";
|
|
51
|
+
import { createProxyDataAdapter } from "@authhero/kysely-adapter";
|
|
52
|
+
|
|
53
|
+
const db = new Kysely({ dialect: /* your dialect */ });
|
|
54
|
+
const app = createProxyApp({
|
|
55
|
+
data: createProxyDataAdapter(db),
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Cloudflare Workers can't open SQLite files, so on Workers this path means D1, Hyperdrive, or a remote MySQL/Postgres. For a local Node process, `better-sqlite3` pointing at the same `db.sqlite` your authhero server uses works out of the box.
|
|
60
|
+
|
|
61
|
+
### HTTP-backed (management API)
|
|
62
|
+
|
|
63
|
+
The proxy authenticates to authhero's management API with a service token. Issue the token from authhero with the scopes `read:proxy_routes` and `read:custom_domains`, then store it as a worker secret:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
wrangler secret put AUTHHERO_SERVICE_TOKEN
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`wrangler.toml` vars:
|
|
70
|
+
|
|
71
|
+
```toml
|
|
72
|
+
[vars]
|
|
73
|
+
AUTHHERO_API_URL = "https://auth.example.com"
|
|
74
|
+
AUTHHERO_TENANT_ID = "example"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then build an adapter that resolves the host via `/api/v2/custom-domains` and the routes via `/api/v2/proxy-routes`:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import type { ProxyDataAdapter, ResolvedHost } from "@authhero/proxy";
|
|
81
|
+
|
|
82
|
+
interface Env {
|
|
83
|
+
AUTHHERO_API_URL: string;
|
|
84
|
+
AUTHHERO_TENANT_ID: string;
|
|
85
|
+
AUTHHERO_SERVICE_TOKEN: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createHttpProxyAdapter(env: Env): ProxyDataAdapter {
|
|
89
|
+
const headers = {
|
|
90
|
+
"authorization": `Bearer ${env.AUTHHERO_SERVICE_TOKEN}`,
|
|
91
|
+
"tenant-id": env.AUTHHERO_TENANT_ID,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
async function api<T>(path: string): Promise<T> {
|
|
95
|
+
const res = await fetch(`${env.AUTHHERO_API_URL}${path}`, { headers });
|
|
96
|
+
if (!res.ok) throw new Error(`${path}: ${res.status}`);
|
|
97
|
+
return res.json() as Promise<T>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
// The proxy data plane only needs resolveHost; the CRUD methods on
|
|
102
|
+
// proxyRoutes stay unused (writes always go through authhero directly).
|
|
103
|
+
proxyRoutes: {
|
|
104
|
+
list: () => { throw new Error("read-only proxy adapter"); },
|
|
105
|
+
get: () => { throw new Error("read-only proxy adapter"); },
|
|
106
|
+
create: () => { throw new Error("read-only proxy adapter"); },
|
|
107
|
+
update: () => { throw new Error("read-only proxy adapter"); },
|
|
108
|
+
remove: () => { throw new Error("read-only proxy adapter"); },
|
|
109
|
+
},
|
|
110
|
+
async resolveHost(host): Promise<ResolvedHost | null> {
|
|
111
|
+
const domains = await api<{ custom_domains: Array<{
|
|
112
|
+
custom_domain_id: string; domain: string;
|
|
113
|
+
}> }>("/api/v2/custom-domains");
|
|
114
|
+
const match = domains.custom_domains.find((d) => d.domain === host);
|
|
115
|
+
if (!match) return null;
|
|
116
|
+
|
|
117
|
+
const routes = await api<{ proxy_routes: unknown[] }>(
|
|
118
|
+
`/api/v2/proxy-routes?custom_domain_id=${match.custom_domain_id}&per_page=200`,
|
|
119
|
+
);
|
|
120
|
+
return {
|
|
121
|
+
tenant_id: env.AUTHHERO_TENANT_ID,
|
|
122
|
+
custom_domain_id: match.custom_domain_id,
|
|
123
|
+
domain: host,
|
|
124
|
+
routes: routes.proxy_routes as ResolvedHost["routes"],
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Cache aggressively (see below) — every cold cache miss is two API round-trips.
|
|
132
|
+
|
|
26
133
|
## Caching
|
|
27
134
|
|
|
28
135
|
Resolved hosts are cached in-memory per Worker isolate with a stale-while-revalidate strategy:
|
|
@@ -31,7 +138,7 @@ Resolved hosts are cached in-memory per Worker isolate with a stale-while-revali
|
|
|
31
138
|
- **Stale** for the next hour — served from cache while a background refresh runs.
|
|
32
139
|
- **Negative** (host not found) — cached for 30 seconds so newly-added hosts come online quickly.
|
|
33
140
|
|
|
34
|
-
For the static adapter the "refresh" is just a re-read of the in-memory config, so the SWR window mainly matters when you swap to
|
|
141
|
+
For the static adapter the "refresh" is just a re-read of the in-memory config, so the SWR window mainly matters when you swap to the HTTP- or database-backed adapter.
|
|
35
142
|
|
|
36
143
|
## Develop locally
|
|
37
144
|
|
|
@@ -2,7 +2,11 @@ import type { StaticProxyAdapterOptions } from "@authhero/proxy";
|
|
|
2
2
|
|
|
3
3
|
// Map each public hostname to the routes the proxy should serve for it.
|
|
4
4
|
// Routes are matched in priority order (lower priority wins). The path
|
|
5
|
-
// pattern supports `*` and `:param` segments.
|
|
5
|
+
// pattern in `match.path` supports `*` and `:param` segments (Hono syntax).
|
|
6
|
+
//
|
|
7
|
+
// Each route is an ordered list of handlers. The last handler is the
|
|
8
|
+
// terminal — it produces the response (e.g. `http`, `redirect`, `static`,
|
|
9
|
+
// `service_binding`). Earlier handlers wrap it, like Hono middleware.
|
|
6
10
|
//
|
|
7
11
|
// Edit this file to add your hosts, then re-deploy.
|
|
8
12
|
export const proxyConfig: StaticProxyAdapterOptions = {
|
|
@@ -11,10 +15,16 @@ export const proxyConfig: StaticProxyAdapterOptions = {
|
|
|
11
15
|
tenant_id: "example",
|
|
12
16
|
routes: [
|
|
13
17
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
match: { path: "/*" },
|
|
19
|
+
handlers: [
|
|
20
|
+
{
|
|
21
|
+
type: "http",
|
|
22
|
+
options: {
|
|
23
|
+
upstream_url: "https://upstream.example.com",
|
|
24
|
+
preserve_host: false,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
18
28
|
},
|
|
19
29
|
],
|
|
20
30
|
},
|