cloudflare-access 1.0.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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +452 -0
  3. package/dist/adapters/effect/index.d.mts +167 -0
  4. package/dist/adapters/effect/index.d.ts +167 -0
  5. package/dist/adapters/effect/index.js +221 -0
  6. package/dist/adapters/effect/index.js.map +1 -0
  7. package/dist/adapters/effect/index.mjs +221 -0
  8. package/dist/adapters/effect/index.mjs.map +1 -0
  9. package/dist/adapters/express/index.d.mts +74 -0
  10. package/dist/adapters/express/index.d.ts +74 -0
  11. package/dist/adapters/express/index.js +129 -0
  12. package/dist/adapters/express/index.js.map +1 -0
  13. package/dist/adapters/express/index.mjs +129 -0
  14. package/dist/adapters/express/index.mjs.map +1 -0
  15. package/dist/adapters/fastify/index.d.mts +111 -0
  16. package/dist/adapters/fastify/index.d.ts +111 -0
  17. package/dist/adapters/fastify/index.js +140 -0
  18. package/dist/adapters/fastify/index.js.map +1 -0
  19. package/dist/adapters/fastify/index.mjs +140 -0
  20. package/dist/adapters/fastify/index.mjs.map +1 -0
  21. package/dist/adapters/hono/index.d.mts +19 -0
  22. package/dist/adapters/hono/index.d.ts +19 -0
  23. package/dist/adapters/hono/index.js +45 -0
  24. package/dist/adapters/hono/index.js.map +1 -0
  25. package/dist/adapters/hono/index.mjs +45 -0
  26. package/dist/adapters/hono/index.mjs.map +1 -0
  27. package/dist/adapters/nestjs/index.d.mts +123 -0
  28. package/dist/adapters/nestjs/index.d.ts +123 -0
  29. package/dist/adapters/nestjs/index.js +117 -0
  30. package/dist/adapters/nestjs/index.js.map +1 -0
  31. package/dist/adapters/nestjs/index.mjs +117 -0
  32. package/dist/adapters/nestjs/index.mjs.map +1 -0
  33. package/dist/chunk-DM2KGIQX.mjs +320 -0
  34. package/dist/chunk-DM2KGIQX.mjs.map +1 -0
  35. package/dist/chunk-LQWCGHLJ.mjs +108 -0
  36. package/dist/chunk-LQWCGHLJ.mjs.map +1 -0
  37. package/dist/chunk-PMFPT3SI.js +108 -0
  38. package/dist/chunk-PMFPT3SI.js.map +1 -0
  39. package/dist/chunk-WUJPWM4T.js +320 -0
  40. package/dist/chunk-WUJPWM4T.js.map +1 -0
  41. package/dist/config-D4O7DXNT.d.mts +12 -0
  42. package/dist/config-ottUdc-K.d.ts +12 -0
  43. package/dist/core/index.d.mts +24 -0
  44. package/dist/core/index.d.ts +24 -0
  45. package/dist/core/index.js +41 -0
  46. package/dist/core/index.js.map +1 -0
  47. package/dist/core/index.mjs +41 -0
  48. package/dist/core/index.mjs.map +1 -0
  49. package/dist/index.d.mts +6 -0
  50. package/dist/index.d.ts +6 -0
  51. package/dist/index.js +41 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/index.mjs +41 -0
  54. package/dist/index.mjs.map +1 -0
  55. package/dist/jwks-ChdyyS_L.d.mts +173 -0
  56. package/dist/jwks-ChdyyS_L.d.ts +173 -0
  57. package/dist/middleware-BDl6jUCu.d.mts +83 -0
  58. package/dist/middleware-CgFsjM20.d.ts +83 -0
  59. package/examples/basic.ts +52 -0
  60. package/examples/cloudflare-workers.ts +84 -0
  61. package/examples/custom-handlers.ts +85 -0
  62. package/examples/effect/http-server.ts +205 -0
  63. package/examples/email-allowlist.ts +50 -0
  64. package/examples/express/basic.ts +26 -0
  65. package/examples/fastify/basic.ts +24 -0
  66. package/examples/hono/basic.ts +26 -0
  67. package/examples/hono-router.ts +74 -0
  68. package/examples/nestjs/basic.ts +39 -0
  69. package/examples/skip-dev-mode.ts +89 -0
  70. package/package.json +178 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,452 @@
1
+ # cloudflare-access
2
+
3
+ Multi-framework authentication for Cloudflare Access with JWT validation.
4
+
5
+ Supports: **Hono**, **Express**, **Fastify**, **NestJS**, and **Effect-TS**.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # npm
11
+ npm install cloudflare-access
12
+
13
+ # yarn
14
+ yarn add cloudflare-access
15
+
16
+ # bun
17
+ bun add cloudflare-access
18
+ ```
19
+
20
+ ## Framework-Specific Installation
21
+
22
+ ### Hono
23
+
24
+ ```bash
25
+ bun add cloudflare-access hono
26
+ ```
27
+
28
+ ### Express
29
+
30
+ ```bash
31
+ bun add cloudflare-access express
32
+ ```
33
+
34
+ ### Fastify
35
+
36
+ ```bash
37
+ bun add cloudflare-access fastify
38
+ ```
39
+
40
+ ### NestJS
41
+
42
+ ```bash
43
+ bun add cloudflare-access @nestjs/common @nestjs/core
44
+ ```
45
+
46
+ ### Effect-TS
47
+
48
+ ```bash
49
+ bun add cloudflare-access effect
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### Hono
55
+
56
+ ```typescript
57
+ import { Hono } from "hono";
58
+ import { createCloudflareAccessAuth } from "cloudflare-access/hono";
59
+
60
+ const app = new Hono();
61
+
62
+ // Using static configuration (works with any deployment target)
63
+ app.use(
64
+ createCloudflareAccessAuth({
65
+ accessConfig: {
66
+ teamDomain: process.env.CF_ACCESS_TEAM_DOMAIN!,
67
+ audTag: process.env.CF_ACCESS_AUD!,
68
+ },
69
+ }),
70
+ );
71
+
72
+ // Or with Cloudflare Workers bindings
73
+ // import { getCloudflareAccessConfigFromBindings } from "cloudflare-access/hono";
74
+ // app.use(createCloudflareAccessAuth({
75
+ // accessConfig: getCloudflareAccessConfigFromBindings,
76
+ // }));
77
+
78
+ app.get("/protected", (c) => {
79
+ const user = c.get("user");
80
+ return c.json({ email: user?.email });
81
+ });
82
+ ```
83
+
84
+ ### Express
85
+
86
+ ```typescript
87
+ import express from "express";
88
+ import { cloudflareAccessAuth } from "cloudflare-access/express";
89
+
90
+ const app = express();
91
+
92
+ app.use(
93
+ cloudflareAccessAuth({
94
+ accessConfig: {
95
+ teamDomain: "https://yourteam.cloudflareaccess.com",
96
+ audTag: "your-audience-tag",
97
+ },
98
+ }),
99
+ );
100
+
101
+ app.get("/protected", (req, res) => {
102
+ res.json({ email: req.user?.email });
103
+ });
104
+ ```
105
+
106
+ ### Fastify
107
+
108
+ ```typescript
109
+ import fastify from "fastify";
110
+ import { cloudflareAccessPlugin } from "cloudflare-access/fastify";
111
+
112
+ const app = fastify();
113
+
114
+ app.register(cloudflareAccessPlugin, {
115
+ accessConfig: {
116
+ teamDomain: "https://yourteam.cloudflareaccess.com",
117
+ audTag: "your-audience-tag",
118
+ },
119
+ });
120
+
121
+ app.get("/protected", async (request, reply) => {
122
+ return { email: request.user?.email };
123
+ });
124
+ ```
125
+
126
+ ### NestJS
127
+
128
+ ```typescript
129
+ import { Controller, Get, Req, Module } from "@nestjs/common";
130
+ import { CloudflareAccessGuard, Public } from "cloudflare-access/nestjs";
131
+ import { APP_GUARD } from "@nestjs/core";
132
+ import type { Request } from "express";
133
+
134
+ @Controller("api")
135
+ export class ApiController {
136
+ @Get("protected")
137
+ getProtected(@Req() req: Request) {
138
+ return { email: req.user?.email };
139
+ }
140
+
141
+ @Public()
142
+ @Get("public")
143
+ getPublic() {
144
+ return { message: "This is public" };
145
+ }
146
+ }
147
+
148
+ @Module({
149
+ controllers: [ApiController],
150
+ providers: [
151
+ {
152
+ provide: APP_GUARD,
153
+ useFactory: () =>
154
+ new CloudflareAccessGuard({
155
+ accessConfig: {
156
+ teamDomain: "https://yourteam.cloudflareaccess.com",
157
+ audTag: "your-audience-tag",
158
+ },
159
+ }),
160
+ },
161
+ ],
162
+ })
163
+ export class AppModule {}
164
+ ```
165
+
166
+ ### Effect-TS
167
+
168
+ ```typescript
169
+ import { Effect, Either } from "effect";
170
+ import { HttpServerRequest } from "@effect/platform";
171
+ import { authenticateRequest } from "cloudflare-access/effect";
172
+
173
+ const request: HttpServerRequest.HttpServerRequest = {
174
+ url: "https://example.com/api/protected",
175
+ headers: {
176
+ "cf-access-jwt-assertion": "jwt-token-here",
177
+ },
178
+ method: "GET",
179
+ };
180
+
181
+ const program = Effect.gen(function* () {
182
+ const result = yield* Effect.either(
183
+ authenticateRequest(request, {
184
+ accessConfig: {
185
+ teamDomain: "https://yourteam.cloudflareaccess.com",
186
+ audTag: "your-audience-tag",
187
+ },
188
+ }),
189
+ );
190
+
191
+ return Either.match(result, {
192
+ onLeft: (error) => {
193
+ console.error(`Auth failed: ${error.message}`);
194
+ return null;
195
+ },
196
+ onRight: (user) => {
197
+ console.log(`Authenticated user: ${user.email}`);
198
+ return user;
199
+ },
200
+ });
201
+ });
202
+
203
+ Effect.runPromise(program);
204
+ ```
205
+
206
+ ## Configuration
207
+
208
+ ### Environment Variables
209
+
210
+ Configure your Cloudflare Access credentials via environment variables:
211
+
212
+ ```bash
213
+ # .env
214
+ CF_ACCESS_TEAM_DOMAIN=https://yourteam.cloudflareaccess.com
215
+ CF_ACCESS_AUD=your-audience-tag
216
+ ENVIRONMENT=dev
217
+ ```
218
+
219
+ Or set them directly in your environment:
220
+
221
+ ```bash
222
+ export CF_ACCESS_TEAM_DOMAIN="https://yourteam.cloudflareaccess.com"
223
+ export CF_ACCESS_AUD="your-audience-tag"
224
+ ```
225
+
226
+ For **Cloudflare Workers**, use `wrangler.toml`:
227
+
228
+ ```toml
229
+ [vars]
230
+ CF_ACCESS_TEAM_DOMAIN = "https://yourteam.cloudflareaccess.com"
231
+ CF_ACCESS_AUD = "your-audience-tag"
232
+ ```
233
+
234
+ Or with Wrangler secrets:
235
+
236
+ ```bash
237
+ wrangler secret put CF_ACCESS_AUD
238
+ ```
239
+
240
+ ### Common Options
241
+
242
+ All framework adapters support these options:
243
+
244
+ ```typescript
245
+ interface CloudflareAccessAuthOptions {
246
+ /** Cloudflare Access configuration */
247
+ accessConfig: CloudflareAccessConfig;
248
+
249
+ /** Optional email allowlist */
250
+ allowedEmails?: string[];
251
+
252
+ /** Custom unauthorized handler */
253
+ onUnauthorized?: (reason: string) => Response | void | Promise<void>;
254
+
255
+ /** Custom forbidden handler */
256
+ onForbidden?: (email: string) => Response | void | Promise<void>;
257
+
258
+ /** Paths to exclude from authentication */
259
+ excludePaths?: string[];
260
+
261
+ /** Skip JWT validation in development (localhost requests) */
262
+ skipInDev?: boolean;
263
+
264
+ /** Environment indicator */
265
+ environment?: string;
266
+ }
267
+ ```
268
+
269
+ ## User Information
270
+
271
+ After successful authentication, user information is available:
272
+
273
+ ```typescript
274
+ interface CloudflareAccessUser {
275
+ email: string;
276
+ userId?: string;
277
+ country?: string;
278
+ }
279
+ ```
280
+
281
+ ## Rich Error Handling
282
+
283
+ The library provides rich, actionable errors with detailed context:
284
+
285
+ ### Error Classes
286
+
287
+ ```typescript
288
+ import {
289
+ CloudflareAccessError,
290
+ AuthRequiredError,
291
+ InvalidTokenError,
292
+ AccessDeniedError,
293
+ ConfigurationError,
294
+ // Type guards
295
+ isAuthRequiredError,
296
+ isInvalidTokenError,
297
+ isAccessDeniedError,
298
+ } from "cloudflare-access/core";
299
+
300
+ // All errors extend CloudflareAccessError
301
+ try {
302
+ await validateCloudflareAccessToken(token, options, url);
303
+ } catch (error) {
304
+ if (isAuthRequiredError(error)) {
305
+ console.log("Auth required:", error.message);
306
+ console.log("Fix:", error.context?.fix);
307
+ }
308
+
309
+ if (isInvalidTokenError(error)) {
310
+ console.log("Invalid token:", error.reason);
311
+ console.log("Token info:", error.context?.tokenInfo);
312
+ }
313
+
314
+ if (isAccessDeniedError(error)) {
315
+ console.log("Access denied for:", error.email);
316
+ }
317
+
318
+ // All errors are serializable
319
+ console.log("Full error:", error.toJSON());
320
+ }
321
+ ```
322
+
323
+ ### Error Codes
324
+
325
+ ```typescript
326
+ import { CloudflareAccessErrorCode } from "cloudflare-access/core";
327
+
328
+ // Available error codes:
329
+ // - AUTH_REQUIRED: Missing or invalid authentication token
330
+ // - INVALID_TOKEN: Token validation failed (expired, wrong signature, etc.)
331
+ // - ACCESS_DENIED: User email not in allowlist
332
+ // - INVALID_TEAM_DOMAIN: Invalid team domain configuration
333
+ // - MISSING_AUDIENCE_TAG: Missing audience tag
334
+ // - MISSING_CONFIG: Missing environment configuration
335
+ ```
336
+
337
+ ### Effect-TS Error Handling
338
+
339
+ ```typescript
340
+ import { Effect, Either } from "effect";
341
+ import {
342
+ authenticateRequest,
343
+ AuthRequiredError,
344
+ InvalidTokenError,
345
+ AccessDeniedError,
346
+ } from "cloudflare-access/effect";
347
+ import { HttpServerRequest } from "@effect/platform";
348
+
349
+ const request: HttpServerRequest.HttpServerRequest = {
350
+ url: "https://example.com/api/protected",
351
+ headers: { "cf-access-jwt-assertion": "token" },
352
+ method: "GET",
353
+ };
354
+
355
+ const program = Effect.gen(function* () {
356
+ const result = yield* Effect.either(
357
+ authenticateRequest(request, {
358
+ accessConfig: {
359
+ teamDomain: "https://yourteam.cloudflareaccess.com",
360
+ audTag: "your-audience-tag",
361
+ },
362
+ }),
363
+ );
364
+
365
+ return Either.match(result, {
366
+ onLeft: (error) => {
367
+ if (error instanceof AuthRequiredError) {
368
+ console.log("Auth required:", error.message);
369
+ return null;
370
+ }
371
+ if (error instanceof AccessDeniedError) {
372
+ console.log("Access denied for:", error.email);
373
+ return null;
374
+ }
375
+ throw error;
376
+ },
377
+ onRight: (user) => user,
378
+ });
379
+ });
380
+ ```
381
+
382
+ ## Using the Core Module Directly
383
+
384
+ If you need lower-level access to the authentication logic:
385
+
386
+ ```typescript
387
+ import {
388
+ validateCloudflareAccessToken,
389
+ getCloudflareAccessConfigFromEnv,
390
+ type CloudflareAccessConfig,
391
+ } from "cloudflare-access/core";
392
+
393
+ const result = await validateCloudflareAccessToken(
394
+ token,
395
+ { accessConfig: { teamDomain, audTag } },
396
+ requestUrl,
397
+ );
398
+
399
+ if (result.success) {
400
+ console.log("User:", result.user);
401
+ } else {
402
+ console.log("Auth failed:", result.error);
403
+ }
404
+ ```
405
+
406
+ ## Package Imports
407
+
408
+ The package uses subpath exports for each framework:
409
+
410
+ ```typescript
411
+ // Core authentication logic
412
+ import { validateCloudflareAccessToken } from "cloudflare-access/core";
413
+
414
+ // Framework adapters
415
+ import { createCloudflareAccessAuth } from "cloudflare-access/hono";
416
+ import { cloudflareAccessAuth } from "cloudflare-access/express";
417
+ import { cloudflareAccessPlugin } from "cloudflare-access/fastify";
418
+ import { CloudflareAccessGuard } from "cloudflare-access/nestjs";
419
+ import { authenticateRequest } from "cloudflare-access/effect";
420
+
421
+ // Hono exports (from root)
422
+ import {
423
+ createCloudflareAccessAuth,
424
+ getCloudflareAccessConfigFromBindings,
425
+ } from "cloudflare-access";
426
+ ```
427
+
428
+ ## Examples
429
+
430
+ See the `/examples` directory for detailed examples:
431
+
432
+ - `examples/hono/` - Hono framework examples
433
+ - `examples/express/` - Express framework examples
434
+ - `examples/fastify/` - Fastify framework examples
435
+ - `examples/nestjs/` - NestJS framework examples
436
+ - `examples/effect/` - Effect-TS framework examples
437
+
438
+ ## Testing
439
+
440
+ The library includes comprehensive test coverage for all adapters:
441
+
442
+ ```bash
443
+ # Run all tests
444
+ bun test
445
+
446
+ # Run with coverage
447
+ bun test --coverage
448
+ ```
449
+
450
+ ## License
451
+
452
+ MIT
@@ -0,0 +1,167 @@
1
+ import { C as CloudflareAccessConfig, g as CloudflareAccessUser, c as CloudflareAccessError } from '../../jwks-ChdyyS_L.mjs';
2
+ export { A as AccessDeniedError, a as AuthRequiredError, b as AuthResult, d as CloudflareAccessErrorCode, e as CloudflareAccessMiddlewareEnv, f as CloudflareAccessPayload, h as ConfigurationError, I as InvalidTokenError, _ as __clearJwksCache, i as isAccessDeniedError, j as isAuthRequiredError, k as isCloudflareAccessError, l as isConfigurationError, m as isInvalidTokenError, t as toAuthError } from '../../jwks-ChdyyS_L.mjs';
3
+ export { g as getCloudflareAccessConfigFromEnv } from '../../config-D4O7DXNT.mjs';
4
+ import { Schema, Context, Redacted, Effect, Layer, Option } from 'effect';
5
+ import * as _effect_platform_HttpRouter from '@effect/platform/HttpRouter';
6
+ import { HttpApiMiddleware, HttpApiSecurity, HttpServerRequest } from '@effect/platform';
7
+ import * as effect_Either from 'effect/Either';
8
+ import 'jose';
9
+
10
+ /**
11
+ * Options for creating Cloudflare Access middleware
12
+ */
13
+ interface CloudflareAccessMiddlewareOptions {
14
+ /** Cloudflare Access configuration */
15
+ accessConfig: CloudflareAccessConfig;
16
+ /** Optional email allowlist */
17
+ allowedEmails?: string[];
18
+ /** Skip validation in development */
19
+ skipInDev?: boolean;
20
+ /** Environment identifier */
21
+ environment?: string;
22
+ }
23
+
24
+ declare const Unauthorized_base: Schema.TaggedErrorClass<Unauthorized, "Unauthorized", {
25
+ readonly _tag: Schema.tag<"Unauthorized">;
26
+ } & {
27
+ message: typeof Schema.String;
28
+ code: typeof Schema.String;
29
+ }>;
30
+ /**
31
+ * Unauthorized error schema for Effect Platform
32
+ */
33
+ declare class Unauthorized extends Unauthorized_base {
34
+ }
35
+ declare const Forbidden_base: Schema.TaggedErrorClass<Forbidden, "Forbidden", {
36
+ readonly _tag: Schema.tag<"Forbidden">;
37
+ } & {
38
+ message: typeof Schema.String;
39
+ email: typeof Schema.String;
40
+ }>;
41
+ /**
42
+ * Forbidden error schema for Effect Platform
43
+ */
44
+ declare class Forbidden extends Forbidden_base {
45
+ }
46
+
47
+ declare const CurrentUser_base: Context.TagClass<CurrentUser, "cloudflare-access/CurrentUser", CloudflareAccessUser>;
48
+ /**
49
+ * Context Tag for the authenticated user.
50
+ * Use this to access the current user in your handlers.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { Effect } from "effect";
55
+ * import { CurrentUser } from "cloudflare-access/effect";
56
+ *
57
+ * const handler = Effect.gen(function* () {
58
+ * const user = yield* CurrentUser;
59
+ * console.log(`Hello ${user.email}`);
60
+ * return user;
61
+ * });
62
+ * ```
63
+ */
64
+ declare class CurrentUser extends CurrentUser_base {
65
+ }
66
+
67
+ declare const CloudflareAccessAuth_base: HttpApiMiddleware.TagClass.BaseSecurity<CloudflareAccessAuth, "CloudflareAccessAuth", {
68
+ readonly provides: typeof CurrentUser;
69
+ readonly failure: Schema.Union<[typeof Unauthorized, typeof Forbidden]>;
70
+ readonly security: {
71
+ readonly bearer: HttpApiSecurity.Bearer;
72
+ };
73
+ }, {
74
+ readonly bearer: (_: Redacted.Redacted<string>) => Effect.Effect<CloudflareAccessUser, Unauthorized | Forbidden, _effect_platform_HttpRouter.HttpRouter.Provided>;
75
+ }, {
76
+ readonly bearer: HttpApiSecurity.Bearer;
77
+ }>;
78
+ /**
79
+ * Cloudflare Access authentication middleware class.
80
+ *
81
+ * Use this class directly in your API middleware definitions.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { HttpApi, HttpApiGroup, HttpApiEndpoint } from "@effect/platform";
86
+ * import { Schema, Effect, Layer } from "effect";
87
+ * import {
88
+ * CloudflareAccessAuth,
89
+ * CurrentUser,
90
+ * makeCloudflareAccessLive,
91
+ * Unauthorized,
92
+ * Forbidden
93
+ * } from "cloudflare-access/effect";
94
+ *
95
+ * // Configure the middleware
96
+ * const CloudflareAccessLive = makeCloudflareAccessLive({
97
+ * accessConfig: {
98
+ * teamDomain: "https://yourteam.cloudflareaccess.com",
99
+ * audTag: "your-audience-tag",
100
+ * },
101
+ * allowedEmails: ["admin@example.com"],
102
+ * skipInDev: true,
103
+ * environment: "dev",
104
+ * });
105
+ *
106
+ * // Define API with protected endpoints
107
+ * const User = Schema.Struct({ email: Schema.String });
108
+ *
109
+ * const api = HttpApi.make("api").add(
110
+ * HttpApiGroup.make("users").add(
111
+ * HttpApiEndpoint.get("profile", "/profile")
112
+ * .addSuccess(User)
113
+ * .middleware(CloudflareAccessAuth)
114
+ * )
115
+ * );
116
+ *
117
+ * // Implement handlers with access to CurrentUser
118
+ * const UsersLive = HttpApiBuilder.group(api, "users", (handlers) =>
119
+ * Effect.gen(function* () {
120
+ * const user = yield* CurrentUser;
121
+ * return handlers.handle("profile", () =>
122
+ * Effect.succeed({ email: user.email })
123
+ * );
124
+ * })
125
+ * ).pipe(Layer.provide(CloudflareAccessLive));
126
+ * ```
127
+ */
128
+ declare class CloudflareAccessAuth extends CloudflareAccessAuth_base {
129
+ }
130
+ /**
131
+ * Create a Layer that implements Cloudflare Access authentication.
132
+ *
133
+ * This layer validates the Cloudflare Access JWT token from the request
134
+ * and provides the authenticated user via the CurrentUser context.
135
+ *
136
+ * @param options Configuration for Cloudflare Access
137
+ * @returns Layer that provides authentication
138
+ */
139
+ declare const makeCloudflareAccessLive: (options: CloudflareAccessMiddlewareOptions) => Layer.Layer<CloudflareAccessAuth, never, never>;
140
+
141
+ /**
142
+ * Extract Cloudflare Access token from request headers.
143
+ * Looks for CF-Access-JWT-Assertion header.
144
+ *
145
+ * @param request HTTP server request
146
+ * @returns Option with token if present
147
+ */
148
+ declare const extractToken: (request: HttpServerRequest.HttpServerRequest) => Option.Option<string>;
149
+ /**
150
+ * Authenticate using the CF-Access-JWT-Assertion header directly.
151
+ * This bypasses the HttpApiSecurity bearer token pattern.
152
+ *
153
+ * @param request HTTP server request
154
+ * @param options Authentication options
155
+ * @returns Effect with authenticated user or error
156
+ */
157
+ declare const authenticateRequest: (request: HttpServerRequest.HttpServerRequest, options: CloudflareAccessMiddlewareOptions) => Effect.Effect<CloudflareAccessUser, CloudflareAccessError, never>;
158
+ /**
159
+ * Get user as Option (returns None on auth failure)
160
+ */
161
+ declare const getUser: (request: HttpServerRequest.HttpServerRequest, options: CloudflareAccessMiddlewareOptions) => Effect.Effect<Option.Option<CloudflareAccessUser>, never, never>;
162
+ /**
163
+ * Authenticate and return Either (for explicit error handling)
164
+ */
165
+ declare const authenticateEither: (request: HttpServerRequest.HttpServerRequest, options: CloudflareAccessMiddlewareOptions) => Effect.Effect<effect_Either.Either<CloudflareAccessUser, CloudflareAccessError>, never, never>;
166
+
167
+ export { CloudflareAccessAuth, CloudflareAccessConfig, CloudflareAccessError, type CloudflareAccessMiddlewareOptions, CloudflareAccessUser, CurrentUser, Forbidden, Unauthorized, authenticateEither, authenticateRequest, extractToken, getUser, makeCloudflareAccessLive };