@authhero/cloudflare-adapter 2.8.0 → 2.10.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/README.md +171 -8
- package/dist/cloudflare-adapter.cjs +10 -10
- package/dist/cloudflare-adapter.d.ts +132 -13
- package/dist/cloudflare-adapter.mjs +1529 -1361
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,10 +4,11 @@ Cloudflare-specific adapters for AuthHero, providing integrations with Cloudflar
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
This package provides
|
|
7
|
+
This package provides four adapters:
|
|
8
8
|
|
|
9
9
|
- **Custom Domains** - Manage custom domains via Cloudflare API
|
|
10
10
|
- **Cache** - Caching using Cloudflare's Cache API
|
|
11
|
+
- **Geo** (optional) - Extract geographic information from Cloudflare request headers
|
|
11
12
|
- **Logs** (optional) - Write authentication logs to Cloudflare R2 using Pipelines and query with R2 SQL
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
@@ -35,6 +36,13 @@ const adapters = createAdapters({
|
|
|
35
36
|
defaultTtlSeconds: 3600,
|
|
36
37
|
keyPrefix: "authhero:",
|
|
37
38
|
|
|
39
|
+
// Geo adapter configuration (optional) - automatically included when getHeaders is provided
|
|
40
|
+
getHeaders: () => {
|
|
41
|
+
// In Cloudflare Workers, you'd typically pass request headers
|
|
42
|
+
// Cloudflare automatically adds cf-ipcountry, cf-ipcity, etc.
|
|
43
|
+
return request.headers;
|
|
44
|
+
},
|
|
45
|
+
|
|
38
46
|
// R2 SQL logs configuration (optional) - HTTP mode
|
|
39
47
|
r2SqlLogs: {
|
|
40
48
|
pipelineEndpoint: "https://your-stream-id.ingest.cloudflare.com",
|
|
@@ -46,7 +54,7 @@ const adapters = createAdapters({
|
|
|
46
54
|
});
|
|
47
55
|
|
|
48
56
|
// Use the adapters
|
|
49
|
-
const { customDomains, cache, logs } = adapters;
|
|
57
|
+
const { customDomains, cache, geo, logs } = adapters;
|
|
50
58
|
```
|
|
51
59
|
|
|
52
60
|
### Service Binding Mode (Cloudflare Workers)
|
|
@@ -66,6 +74,9 @@ export default {
|
|
|
66
74
|
authEmail: "your-cloudflare-email",
|
|
67
75
|
customDomainAdapter: yourDatabaseCustomDomainsAdapter,
|
|
68
76
|
|
|
77
|
+
// Geo adapter - extract location from Cloudflare headers
|
|
78
|
+
getHeaders: () => Object.fromEntries(request.headers),
|
|
79
|
+
|
|
69
80
|
// R2 SQL logs with service binding
|
|
70
81
|
r2SqlLogs: {
|
|
71
82
|
pipelineBinding: env.PIPELINE_SERVICE,
|
|
@@ -74,7 +85,7 @@ export default {
|
|
|
74
85
|
},
|
|
75
86
|
});
|
|
76
87
|
|
|
77
|
-
// Use adapters.logs
|
|
88
|
+
// Use adapters.logs and adapters.geo
|
|
78
89
|
},
|
|
79
90
|
};
|
|
80
91
|
```
|
|
@@ -225,7 +236,15 @@ In the Cloudflare Dashboard:
|
|
|
225
236
|
{ "name": "strategy_type", "type": "string", "required": false },
|
|
226
237
|
{ "name": "hostname", "type": "string", "required": false },
|
|
227
238
|
{ "name": "auth0_client", "type": "string", "required": false },
|
|
228
|
-
{ "name": "log_id", "type": "string", "required": true }
|
|
239
|
+
{ "name": "log_id", "type": "string", "required": true },
|
|
240
|
+
{ "name": "country_code", "type": "string", "required": false },
|
|
241
|
+
{ "name": "country_code3", "type": "string", "required": false },
|
|
242
|
+
{ "name": "country_name", "type": "string", "required": false },
|
|
243
|
+
{ "name": "city_name", "type": "string", "required": false },
|
|
244
|
+
{ "name": "latitude", "type": "string", "required": false },
|
|
245
|
+
{ "name": "longitude", "type": "string", "required": false },
|
|
246
|
+
{ "name": "time_zone", "type": "string", "required": false },
|
|
247
|
+
{ "name": "continent_code", "type": "string", "required": false }
|
|
229
248
|
]
|
|
230
249
|
}
|
|
231
250
|
```
|
|
@@ -283,24 +302,24 @@ Use this mode when running inside a Cloudflare Worker with a service binding to
|
|
|
283
302
|
```typescript
|
|
284
303
|
// wrangler.toml
|
|
285
304
|
[[pipelines]];
|
|
286
|
-
binding = "
|
|
305
|
+
binding = "AUTHHERO_LOGS_STREAM";
|
|
287
306
|
pipeline = "my-pipeline";
|
|
288
307
|
|
|
289
308
|
// TypeScript
|
|
290
309
|
interface Env {
|
|
291
|
-
|
|
310
|
+
AUTHHERO_LOGS_STREAM: { fetch: typeof fetch };
|
|
292
311
|
}
|
|
293
312
|
|
|
294
313
|
const { logs } = createAdapters({
|
|
295
314
|
r2SqlLogs: {
|
|
296
|
-
pipelineBinding: env.
|
|
315
|
+
pipelineBinding: env.AUTHHERO_LOGS_STREAM,
|
|
297
316
|
authToken: env.R2_SQL_AUTH_TOKEN,
|
|
298
317
|
warehouseName: env.R2_WAREHOUSE_NAME,
|
|
299
318
|
},
|
|
300
319
|
});
|
|
301
320
|
```
|
|
302
321
|
|
|
303
|
-
This mode is more efficient as it avoids HTTP overhead for Worker-to-Worker communication.
|
|
322
|
+
This mode is more efficient as it avoids HTTP overhead for Worker-to-Worker communication. The `AUTHHERO_LOGS_STREAM` binding name is recommended for consistency across workers.
|
|
304
323
|
|
|
305
324
|
##### 3. Passthrough Mode (Wrap Another Adapter)
|
|
306
325
|
|
|
@@ -385,6 +404,150 @@ npx wrangler r2 sql query "your_warehouse" "
|
|
|
385
404
|
"
|
|
386
405
|
```
|
|
387
406
|
|
|
407
|
+
## Geo Adapter
|
|
408
|
+
|
|
409
|
+
The Cloudflare Geo adapter extracts geographic location information from Cloudflare's automatic request headers. This is used to enrich authentication logs with location data.
|
|
410
|
+
|
|
411
|
+
### Features
|
|
412
|
+
|
|
413
|
+
- **Zero Latency**: Uses headers already provided by Cloudflare Workers
|
|
414
|
+
- **No API Calls**: No external services or databases required
|
|
415
|
+
- **Comprehensive Data**: Includes country, city, coordinates, timezone, and continent
|
|
416
|
+
- **Automatic**: Cloudflare populates headers automatically for every request
|
|
417
|
+
|
|
418
|
+
### Configuration
|
|
419
|
+
|
|
420
|
+
The geo adapter is automatically created when you provide the `getHeaders` function:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
const adapters = createAdapters({
|
|
424
|
+
// ... other config
|
|
425
|
+
getHeaders: () => Object.fromEntries(request.headers),
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Access the geo adapter
|
|
429
|
+
const geoInfo = await adapters.geo?.getGeoInfo();
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Cloudflare Headers Used
|
|
433
|
+
|
|
434
|
+
The adapter reads these Cloudflare-provided headers:
|
|
435
|
+
|
|
436
|
+
| Header | Description | Example |
|
|
437
|
+
| ---------------- | ------------------------- | --------------------- |
|
|
438
|
+
| `cf-ipcountry` | 2-letter ISO country code | `US` |
|
|
439
|
+
| `cf-ipcity` | City name | `San Francisco` |
|
|
440
|
+
| `cf-iplatitude` | Latitude coordinate | `37.7749` |
|
|
441
|
+
| `cf-iplongitude` | Longitude coordinate | `-122.4194` |
|
|
442
|
+
| `cf-timezone` | IANA timezone identifier | `America/Los_Angeles` |
|
|
443
|
+
| `cf-ipcontinent` | 2-letter continent code | `NA` |
|
|
444
|
+
|
|
445
|
+
### Response Format
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
interface GeoInfo {
|
|
449
|
+
country_code: string; // "US"
|
|
450
|
+
country_code3: string; // "USA"
|
|
451
|
+
country_name: string; // "United States"
|
|
452
|
+
city_name: string; // "San Francisco"
|
|
453
|
+
latitude: string; // "37.7749"
|
|
454
|
+
longitude: string; // "-122.4194"
|
|
455
|
+
time_zone: string; // "America/Los_Angeles"
|
|
456
|
+
continent_code: string; // "NA"
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Integration with AuthHero
|
|
461
|
+
|
|
462
|
+
When configured in AuthHero, the geo adapter automatically enriches authentication logs:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import { init } from "@authhero/authhero";
|
|
466
|
+
import createAdapters from "@authhero/cloudflare-adapter";
|
|
467
|
+
|
|
468
|
+
const cloudflareAdapters = createAdapters({
|
|
469
|
+
getHeaders: () => Object.fromEntries(request.headers),
|
|
470
|
+
// ... other config
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const authhero = init({
|
|
474
|
+
data: yourDatabaseAdapter,
|
|
475
|
+
geo: cloudflareAdapters.geo, // Add geo adapter
|
|
476
|
+
// ... other config
|
|
477
|
+
});
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Logs will automatically include `location_info`:
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"type": "s",
|
|
485
|
+
"date": "2025-11-28T12:00:00.000Z",
|
|
486
|
+
"location_info": {
|
|
487
|
+
"country_code": "US",
|
|
488
|
+
"country_code3": "USA",
|
|
489
|
+
"country_name": "United States",
|
|
490
|
+
"city_name": "San Francisco",
|
|
491
|
+
"latitude": "37.7749",
|
|
492
|
+
"longitude": "-122.4194",
|
|
493
|
+
"time_zone": "America/Los_Angeles",
|
|
494
|
+
"continent_code": "NA"
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Alternative: IP Geolocation Databases
|
|
500
|
+
|
|
501
|
+
If you're not using Cloudflare Workers or need more detailed location data, you can implement a custom `GeoAdapter` using IP geolocation databases like MaxMind GeoIP2:
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
import maxmind from "maxmind";
|
|
505
|
+
import { GeoAdapter, GeoInfo } from "@authhero/adapter-interfaces";
|
|
506
|
+
|
|
507
|
+
class MaxMindGeoAdapter implements GeoAdapter {
|
|
508
|
+
private reader: maxmind.Reader<maxmind.CityResponse>;
|
|
509
|
+
|
|
510
|
+
private constructor(reader: maxmind.Reader<maxmind.CityResponse>) {
|
|
511
|
+
this.reader = reader;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
static async create(databasePath: string): Promise<MaxMindGeoAdapter> {
|
|
515
|
+
const reader = await maxmind.open<maxmind.CityResponse>(databasePath);
|
|
516
|
+
return new MaxMindGeoAdapter(reader);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async getGeoInfo(): Promise<GeoInfo | null> {
|
|
520
|
+
const ip = this.getClientIP();
|
|
521
|
+
const lookup = this.reader.get(ip);
|
|
522
|
+
|
|
523
|
+
if (!lookup) return null;
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
country_code: lookup.country?.iso_code || "",
|
|
527
|
+
country_code3: lookup.country?.iso_code3 || "",
|
|
528
|
+
country_name: lookup.country?.names?.en || "",
|
|
529
|
+
city_name: lookup.city?.names?.en || "",
|
|
530
|
+
latitude: lookup.location?.latitude?.toString() || "",
|
|
531
|
+
longitude: lookup.location?.longitude?.toString() || "",
|
|
532
|
+
time_zone: lookup.location?.time_zone || "",
|
|
533
|
+
continent_code: lookup.continent?.code || "",
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Usage:
|
|
539
|
+
const geoAdapter = await MaxMindGeoAdapter.create(
|
|
540
|
+
"/path/to/GeoLite2-City.mmdb",
|
|
541
|
+
);
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Considerations for IP Databases**:
|
|
545
|
+
|
|
546
|
+
- Requires database downloads and regular updates
|
|
547
|
+
- Additional latency for lookups (1-5ms typically)
|
|
548
|
+
- May require licensing fees
|
|
549
|
+
- Works in any environment, not just edge platforms
|
|
550
|
+
|
|
388
551
|
## Environment Variables
|
|
389
552
|
|
|
390
553
|
Recommended environment variables:
|