create-authhero 0.7.0 → 0.9.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-multitenant/.dev.vars.example +20 -4
- package/dist/cloudflare-multitenant/README.md +238 -89
- package/dist/cloudflare-multitenant/drizzle.config.ts +8 -0
- package/dist/cloudflare-multitenant/migrations/0000_init.sql +782 -0
- package/dist/cloudflare-multitenant/migrations/meta/_journal.json +13 -0
- package/dist/cloudflare-multitenant/seed-helper.js +75 -0
- package/dist/cloudflare-multitenant/src/app.ts +3 -22
- package/dist/cloudflare-multitenant/src/db/schema.ts +845 -0
- package/dist/cloudflare-multitenant/src/index.ts +61 -55
- package/dist/cloudflare-multitenant/src/seed.ts +64 -0
- package/dist/cloudflare-multitenant/src/types.ts +17 -17
- package/dist/cloudflare-multitenant/wrangler.toml +40 -28
- package/dist/cloudflare-simple/.dev.vars.example +20 -0
- package/dist/cloudflare-simple/README.md +246 -10
- package/dist/cloudflare-simple/drizzle.config.ts +8 -0
- package/dist/cloudflare-simple/migrations/0000_init.sql +782 -0
- package/dist/cloudflare-simple/migrations/meta/0000_snapshot.json +5325 -0
- package/dist/cloudflare-simple/migrations/meta/_journal.json +13 -0
- package/dist/cloudflare-simple/seed-helper.js +75 -0
- package/dist/cloudflare-simple/src/app.ts +10 -0
- package/dist/cloudflare-simple/src/db/schema.ts +845 -0
- package/dist/cloudflare-simple/src/index.ts +64 -13
- package/dist/cloudflare-simple/src/seed.ts +64 -0
- package/dist/cloudflare-simple/src/types.ts +18 -0
- package/dist/cloudflare-simple/wrangler.toml +29 -0
- package/dist/create-authhero.js +199 -131
- package/package.json +1 -1
- package/dist/cloudflare-multitenant/src/database-factory.ts +0 -220
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Development Environment Variables
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# Copy this file to .dev.vars and fill in your values.
|
|
5
|
+
# The .dev.vars file is used by wrangler for local development.
|
|
6
|
+
# ============================================================================
|
|
7
|
+
|
|
8
|
+
# ============================================================================
|
|
9
|
+
# OPTIONAL: Analytics Engine Configuration
|
|
10
|
+
# ============================================================================
|
|
11
|
+
# Required for Analytics Engine logging. Get these from your Cloudflare Dashboard:
|
|
12
|
+
# - Account ID: Found in the URL when logged into Cloudflare Dashboard
|
|
13
|
+
# - API Token: Create at https://dash.cloudflare.com/profile/api-tokens
|
|
14
|
+
# Required permissions: Account > Workers R2 Storage > Edit (for logs)
|
|
15
|
+
#
|
|
16
|
+
# CLOUDFLARE_ACCOUNT_ID=your_account_id_here
|
|
17
|
+
# CLOUDFLARE_API_TOKEN=your_api_token_here
|
|
4
18
|
|
|
5
19
|
# Optional: Separate token for Analytics Engine (if different from main token)
|
|
6
20
|
# ANALYTICS_ENGINE_API_TOKEN=your_analytics_token_here
|
|
7
21
|
|
|
8
|
-
#
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# OPTIONAL: Other Configuration
|
|
24
|
+
# ============================================================================
|
|
9
25
|
# BASE_DOMAIN=auth.example.com
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A production-grade multi-tenant AuthHero authentication server using Cloudflare Workers with:
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
5
|
+
- Multi-tenant support with tenant isolation at the data level
|
|
6
|
+
- Multiple tenants in a single D1 database
|
|
7
|
+
- Easy setup similar to single-tenant
|
|
8
8
|
|
|
9
9
|
## Architecture
|
|
10
10
|
|
|
@@ -14,34 +14,20 @@ A production-grade multi-tenant AuthHero authentication server using Cloudflare
|
|
|
14
14
|
│ │
|
|
15
15
|
│ ┌─────────────────────────────────┐ │
|
|
16
16
|
│ │ AuthHero Multi-Tenant │ │
|
|
17
|
-
│
|
|
18
|
-
│
|
|
19
|
-
│ ▼ │
|
|
20
|
-
│ ┌─────────────────────────────────┐ │
|
|
21
|
-
│ │ Multi-Tenancy Plugin │ │
|
|
22
|
-
│ │ - Access Control │ │
|
|
23
|
-
│ │ - Subdomain Routing │ │
|
|
24
|
-
│ │ - Database Resolution │ │
|
|
17
|
+
│ │ - Multiple tenants per DB │ │
|
|
18
|
+
│ │ - Tenant isolation via API │ │
|
|
25
19
|
│ └─────────────────────────────────┘ │
|
|
26
20
|
│ │ │
|
|
27
21
|
└──────────────┼──────────────────────────┘
|
|
28
22
|
│
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
│ (Bound) │ │ (via REST) │ │ (Logs) │
|
|
36
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
23
|
+
▼
|
|
24
|
+
┌─────────────┐
|
|
25
|
+
│ D1 │
|
|
26
|
+
│ Database │
|
|
27
|
+
│ (All Tenants)│
|
|
28
|
+
└─────────────┘
|
|
37
29
|
```
|
|
38
30
|
|
|
39
|
-
## Prerequisites
|
|
40
|
-
|
|
41
|
-
- [Cloudflare Account](https://dash.cloudflare.com/sign-up) with Workers Paid plan
|
|
42
|
-
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/)
|
|
43
|
-
- Cloudflare API Token with D1 and Workers permissions
|
|
44
|
-
|
|
45
31
|
## Getting Started
|
|
46
32
|
|
|
47
33
|
1. Install dependencies:
|
|
@@ -50,96 +36,76 @@ A production-grade multi-tenant AuthHero authentication server using Cloudflare
|
|
|
50
36
|
npm install
|
|
51
37
|
```
|
|
52
38
|
|
|
53
|
-
2.
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
wrangler d1 create authhero-main-db
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
3. Update `wrangler.toml` with your database ID.
|
|
60
|
-
|
|
61
|
-
4. Create `.dev.vars` from the example:
|
|
39
|
+
2. Run local database migrations:
|
|
62
40
|
|
|
63
41
|
```bash
|
|
64
|
-
|
|
42
|
+
npm run migrate
|
|
65
43
|
```
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
6. Run local database migrations:
|
|
45
|
+
3. Seed the database with an admin user:
|
|
70
46
|
|
|
71
47
|
```bash
|
|
72
|
-
npm run
|
|
48
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed
|
|
73
49
|
```
|
|
74
50
|
|
|
75
|
-
|
|
51
|
+
4. Start the development server:
|
|
76
52
|
```bash
|
|
77
53
|
npm run dev
|
|
78
54
|
```
|
|
79
55
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Create an API token with the following permissions:
|
|
56
|
+
The server will be available at `https://localhost:3000`.
|
|
83
57
|
|
|
84
|
-
|
|
85
|
-
- **Account** > Workers Analytics Engine > Edit
|
|
86
|
-
- **Zone** > Workers Routes > Edit (if using custom domains)
|
|
58
|
+
## Production Deployment
|
|
87
59
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Per-Tenant Database Isolation
|
|
91
|
-
|
|
92
|
-
Each tenant gets its own D1 database, created dynamically when the tenant is provisioned:
|
|
93
|
-
|
|
94
|
-
- Main tenant uses the bound D1 database (low latency)
|
|
95
|
-
- Other tenants use D1 databases via REST API
|
|
96
|
-
|
|
97
|
-
### Access Control
|
|
98
|
-
|
|
99
|
-
- Users authenticate against the main tenant
|
|
100
|
-
- Organization membership grants access to specific tenants
|
|
101
|
-
- Tokens with `org` claim can access the matching tenant
|
|
60
|
+
1. Create a D1 database:
|
|
102
61
|
|
|
103
|
-
|
|
62
|
+
```bash
|
|
63
|
+
wrangler d1 create authhero-db
|
|
64
|
+
```
|
|
104
65
|
|
|
105
|
-
|
|
66
|
+
2. Update `wrangler.toml` with your database ID.
|
|
106
67
|
|
|
107
|
-
|
|
108
|
-
- `tenant2.auth.example.com` → tenant2
|
|
68
|
+
3. Run migrations on production:
|
|
109
69
|
|
|
110
|
-
|
|
70
|
+
```bash
|
|
71
|
+
npm run db:migrate:remote
|
|
72
|
+
```
|
|
111
73
|
|
|
112
|
-
|
|
113
|
-
[vars]
|
|
114
|
-
BASE_DOMAIN = "auth.example.com"
|
|
115
|
-
```
|
|
74
|
+
4. Seed the production database:
|
|
116
75
|
|
|
117
|
-
|
|
76
|
+
```bash
|
|
77
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed:remote
|
|
78
|
+
```
|
|
118
79
|
|
|
119
|
-
|
|
80
|
+
5. Deploy:
|
|
81
|
+
```bash
|
|
82
|
+
npm run deploy
|
|
83
|
+
```
|
|
120
84
|
|
|
121
|
-
-
|
|
122
|
-
- Real-time analytics
|
|
123
|
-
- Low-latency writes
|
|
85
|
+
## Multi-Tenant Features
|
|
124
86
|
|
|
125
|
-
|
|
87
|
+
### Shared Database Model
|
|
126
88
|
|
|
127
|
-
|
|
89
|
+
All tenants share a single D1 database, with data isolation enforced at the application level. This is simpler to manage and suitable for most use cases.
|
|
128
90
|
|
|
129
|
-
|
|
130
|
-
npm run deploy
|
|
131
|
-
```
|
|
91
|
+
### Creating Additional Tenants
|
|
132
92
|
|
|
133
|
-
|
|
93
|
+
You can create additional tenants using the Management API:
|
|
134
94
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
95
|
+
```bash
|
|
96
|
+
# Get an access token first
|
|
97
|
+
TOKEN=$(curl -s https://your-server/oauth/token \
|
|
98
|
+
-d grant_type=client_credentials \
|
|
99
|
+
-d client_id=default \
|
|
100
|
+
-d client_secret=your-secret \
|
|
101
|
+
-d audience=https://your-server/api/v2/ | jq -r '.access_token')
|
|
138
102
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
103
|
+
# Create a new tenant
|
|
104
|
+
curl -X POST https://your-server/api/v2/tenants \
|
|
105
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
-d '{"id": "tenant2", "name": "Tenant 2"}'
|
|
108
|
+
```
|
|
143
109
|
|
|
144
110
|
## Project Structure
|
|
145
111
|
|
|
@@ -147,13 +113,26 @@ All authentication events are logged to Analytics Engine for:
|
|
|
147
113
|
├── src/
|
|
148
114
|
│ ├── index.ts # Worker entry point
|
|
149
115
|
│ ├── app.ts # AuthHero app configuration
|
|
116
|
+
│ ├── seed.ts # Database seeding worker
|
|
150
117
|
│ ├── types.ts # TypeScript type definitions
|
|
151
|
-
│ └──
|
|
118
|
+
│ └── db/
|
|
119
|
+
│ └── schema.ts # Drizzle schema for migrations
|
|
120
|
+
├── migrations/ # Database migrations
|
|
152
121
|
├── wrangler.toml # Cloudflare Worker configuration
|
|
153
|
-
├── .
|
|
122
|
+
├── drizzle.config.ts # Drizzle configuration
|
|
123
|
+
├── seed-helper.js # Helper script to run seeds
|
|
154
124
|
└── package.json
|
|
155
125
|
```
|
|
156
126
|
|
|
127
|
+
## Database Schema Changes
|
|
128
|
+
|
|
129
|
+
To make schema changes:
|
|
130
|
+
|
|
131
|
+
1. Edit `src/db/schema.ts`
|
|
132
|
+
2. Generate migrations: `npm run db:generate`
|
|
133
|
+
3. Apply locally: `npm run migrate`
|
|
134
|
+
4. Apply to production: `npm run db:migrate:remote`
|
|
135
|
+
|
|
157
136
|
## API Documentation
|
|
158
137
|
|
|
159
138
|
Visit your worker URL with `/docs` to see the Swagger UI documentation.
|
|
@@ -175,3 +154,173 @@ curl https://your-worker.workers.dev/management/tenants \
|
|
|
175
154
|
```
|
|
176
155
|
|
|
177
156
|
For more information, visit [https://authhero.net/docs](https://authhero.net/docs).
|
|
157
|
+
|
|
158
|
+
## Optional Features
|
|
159
|
+
|
|
160
|
+
### Analytics Engine (Centralized Logging)
|
|
161
|
+
|
|
162
|
+
Cloudflare Analytics Engine provides centralized logging for your authentication events. To enable:
|
|
163
|
+
|
|
164
|
+
#### Via Cloudflare Dashboard:
|
|
165
|
+
|
|
166
|
+
1. Go to **Workers & Pages** > **Analytics Engine**
|
|
167
|
+
2. Click **Create a dataset**
|
|
168
|
+
3. Name it `authhero_logs`
|
|
169
|
+
4. Copy the dataset name for your wrangler.toml
|
|
170
|
+
|
|
171
|
+
#### Via Wrangler CLI:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# There's no CLI command to create Analytics Engine datasets yet
|
|
175
|
+
# You must use the Cloudflare Dashboard
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Configuration Steps:
|
|
179
|
+
|
|
180
|
+
1. **Create `.dev.vars` file** for local development:
|
|
181
|
+
|
|
182
|
+
```env
|
|
183
|
+
CLOUDFLARE_ACCOUNT_ID=your_account_id
|
|
184
|
+
CLOUDFLARE_API_TOKEN=your_api_token
|
|
185
|
+
# Optional: separate token for Analytics Engine
|
|
186
|
+
ANALYTICS_ENGINE_API_TOKEN=your_analytics_token
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
2. **Update `wrangler.toml`** - uncomment the Analytics Engine section:
|
|
190
|
+
|
|
191
|
+
```toml
|
|
192
|
+
[[analytics_engine_datasets]]
|
|
193
|
+
binding = "AUTH_LOGS"
|
|
194
|
+
dataset = "authhero_logs"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
3. **Update `src/types.ts`** - uncomment the Analytics Engine types:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { AnalyticsEngineDataset } from "@authhero/cloudflare-adapter";
|
|
201
|
+
|
|
202
|
+
export interface Env {
|
|
203
|
+
AUTH_DB: D1Database;
|
|
204
|
+
AUTH_LOGS: AnalyticsEngineDataset;
|
|
205
|
+
CLOUDFLARE_ACCOUNT_ID: string;
|
|
206
|
+
CLOUDFLARE_API_TOKEN: string;
|
|
207
|
+
ANALYTICS_ENGINE_API_TOKEN?: string;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
4. **Update `src/index.ts`** - uncomment the Cloudflare adapter code:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import createCloudflareAdapters from "@authhero/cloudflare-adapter";
|
|
215
|
+
// ... in the fetch handler:
|
|
216
|
+
const cloudflareAdapters = createCloudflareAdapters({
|
|
217
|
+
accountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
218
|
+
apiToken: env.CLOUDFLARE_API_TOKEN,
|
|
219
|
+
analyticsEngineLogs: {
|
|
220
|
+
analyticsEngineBinding: env.AUTH_LOGS,
|
|
221
|
+
accountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
222
|
+
apiToken: env.ANALYTICS_ENGINE_API_TOKEN || env.CLOUDFLARE_API_TOKEN,
|
|
223
|
+
dataset: "authhero_logs",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
// ... spread into config:
|
|
227
|
+
const config: AuthHeroConfig = {
|
|
228
|
+
dataAdapter,
|
|
229
|
+
...cloudflareAdapters,
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
5. **Install the package**:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm install @authhero/cloudflare-adapter
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
6. **Set secrets for production**:
|
|
240
|
+
```bash
|
|
241
|
+
wrangler secret put CLOUDFLARE_ACCOUNT_ID
|
|
242
|
+
wrangler secret put CLOUDFLARE_API_TOKEN
|
|
243
|
+
# Optional:
|
|
244
|
+
wrangler secret put ANALYTICS_ENGINE_API_TOKEN
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Rate Limiting
|
|
248
|
+
|
|
249
|
+
Cloudflare Rate Limiting helps protect your authentication endpoints from abuse. **Requires Workers Paid plan ($5/month)**.
|
|
250
|
+
|
|
251
|
+
#### Via Cloudflare Dashboard:
|
|
252
|
+
|
|
253
|
+
Rate limiting bindings are configured in `wrangler.toml`, not in the Dashboard.
|
|
254
|
+
|
|
255
|
+
#### Configuration Steps:
|
|
256
|
+
|
|
257
|
+
1. **Ensure you have a Workers Paid plan** - Rate limiting is not available on the free tier.
|
|
258
|
+
|
|
259
|
+
2. **Update `wrangler.toml`** - uncomment the Rate Limiting section:
|
|
260
|
+
|
|
261
|
+
```toml
|
|
262
|
+
[[unsafe.bindings]]
|
|
263
|
+
name = "RATE_LIMITER"
|
|
264
|
+
type = "ratelimit"
|
|
265
|
+
namespace_id = "1001" # Unique namespace ID for this limiter
|
|
266
|
+
simple = { limit = 100, period = 60 } # 100 requests per 60 seconds
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
3. **Update `src/types.ts`** - add the RateLimiter type:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
export interface Env {
|
|
273
|
+
AUTH_DB: D1Database;
|
|
274
|
+
RATE_LIMITER: RateLimiter;
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
4. **Add rate limiting logic in `src/index.ts`**:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
export default {
|
|
282
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
283
|
+
// Get client IP for rate limiting
|
|
284
|
+
const clientIp = request.headers.get("CF-Connecting-IP") || "unknown";
|
|
285
|
+
|
|
286
|
+
// Check rate limit
|
|
287
|
+
const { success } = await env.RATE_LIMITER.limit({ key: clientIp });
|
|
288
|
+
if (!success) {
|
|
289
|
+
return new Response(JSON.stringify({ error: "Rate limit exceeded" }), {
|
|
290
|
+
status: 429,
|
|
291
|
+
headers: { "Content-Type": "application/json" },
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ... rest of the handler
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Rate Limit Configuration Options:
|
|
301
|
+
|
|
302
|
+
| Option | Description | Example |
|
|
303
|
+
| -------------- | ---------------------------------- | -------- |
|
|
304
|
+
| `limit` | Max requests allowed | `100` |
|
|
305
|
+
| `period` | Time window in seconds | `60` |
|
|
306
|
+
| `namespace_id` | Unique ID (string) for the limiter | `"1001"` |
|
|
307
|
+
|
|
308
|
+
#### Advanced Rate Limiting:
|
|
309
|
+
|
|
310
|
+
For more granular control, you can rate limit by:
|
|
311
|
+
|
|
312
|
+
- **Tenant ID**: `{ key: tenantId }`
|
|
313
|
+
- **User ID**: `{ key: userId }`
|
|
314
|
+
- **Endpoint + IP**: `{ key: \`\${pathname}:\${clientIp}\` }`
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Rate limit login attempts more strictly
|
|
318
|
+
if (url.pathname.includes("/oauth/token")) {
|
|
319
|
+
const { success } = await env.LOGIN_RATE_LIMITER.limit({
|
|
320
|
+
key: `login:${clientIp}`,
|
|
321
|
+
});
|
|
322
|
+
if (!success) {
|
|
323
|
+
return new Response("Too many login attempts", { status: 429 });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|