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
@@ -0,0 +1,167 @@
1
+ import { C as CloudflareAccessConfig, g as CloudflareAccessUser, c as CloudflareAccessError } from '../../jwks-ChdyyS_L.js';
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.js';
3
+ export { g as getCloudflareAccessConfigFromEnv } from '../../config-ottUdc-K.js';
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 };
@@ -0,0 +1,221 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ var _chunkWUJPWM4Tjs = require('../../chunk-WUJPWM4T.js');
18
+
19
+ // src/adapters/effect/errors.ts
20
+ var _effect = require('effect');
21
+ var _platform = require('@effect/platform');
22
+ var Unauthorized = class extends _effect.Schema.TaggedError()(
23
+ "Unauthorized",
24
+ {
25
+ message: _effect.Schema.String,
26
+ code: _effect.Schema.String
27
+ },
28
+ _platform.HttpApiSchema.annotations({ status: 401 })
29
+ ) {
30
+ };
31
+ var Forbidden = class extends _effect.Schema.TaggedError()(
32
+ "Forbidden",
33
+ {
34
+ message: _effect.Schema.String,
35
+ email: _effect.Schema.String
36
+ },
37
+ _platform.HttpApiSchema.annotations({ status: 403 })
38
+ ) {
39
+ };
40
+
41
+ // src/adapters/effect/context.ts
42
+
43
+ var CurrentUser = class extends _effect.Context.Tag("cloudflare-access/CurrentUser")() {
44
+ };
45
+
46
+ // src/adapters/effect/middleware.ts
47
+
48
+
49
+ var CloudflareAccessAuth = class extends _platform.HttpApiMiddleware.Tag()(
50
+ "CloudflareAccessAuth",
51
+ {
52
+ provides: CurrentUser,
53
+ failure: _effect.Schema.Union(Unauthorized, Forbidden),
54
+ security: {
55
+ bearer: _platform.HttpApiSecurity.bearer
56
+ }
57
+ }
58
+ ) {
59
+ };
60
+ var makeCloudflareAccessLive = (options) => {
61
+ return _effect.Layer.succeed(
62
+ CloudflareAccessAuth,
63
+ CloudflareAccessAuth.of({
64
+ bearer: (bearerToken) => _effect.Effect.gen(function* () {
65
+ const request = yield* _platform.HttpServerRequest.HttpServerRequest;
66
+ const token = _effect.Redacted.value(bearerToken);
67
+ const result = yield* _effect.Effect.promise(
68
+ () => _chunkWUJPWM4Tjs.validateCloudflareAccessToken.call(void 0,
69
+ token,
70
+ {
71
+ accessConfig: options.accessConfig,
72
+ allowedEmails: options.allowedEmails,
73
+ skipInDev: options.skipInDev,
74
+ environment: options.environment
75
+ },
76
+ request.url
77
+ )
78
+ );
79
+ if (!result.success) {
80
+ const errorCode = _nullishCoalesce(_optionalChain([result, 'access', _ => _.error, 'optionalAccess', _2 => _2.code]), () => ( _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.INVALID_TOKEN));
81
+ const errorMessage = _nullishCoalesce(_optionalChain([result, 'access', _3 => _3.error, 'optionalAccess', _4 => _4.message]), () => ( "Authentication failed"));
82
+ if (errorCode === _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.AUTH_REQUIRED) {
83
+ return yield* _effect.Effect.fail(
84
+ new Unauthorized({ message: errorMessage, code: "AUTH_REQUIRED" })
85
+ );
86
+ }
87
+ if (errorCode === _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.ACCESS_DENIED) {
88
+ return yield* _effect.Effect.fail(
89
+ new Forbidden({
90
+ message: errorMessage,
91
+ email: _nullishCoalesce(_optionalChain([result, 'access', _5 => _5.user, 'optionalAccess', _6 => _6.email]), () => ( "unknown"))
92
+ })
93
+ );
94
+ }
95
+ return yield* _effect.Effect.fail(new Unauthorized({ message: errorMessage, code: errorCode }));
96
+ }
97
+ if (!result.user) {
98
+ return {
99
+ email: "dev@example.com",
100
+ userId: "dev-user",
101
+ country: "dev"
102
+ };
103
+ }
104
+ return result.user;
105
+ })
106
+ })
107
+ );
108
+ };
109
+
110
+ // src/adapters/effect/utils.ts
111
+
112
+ var extractToken = (request) => {
113
+ const token = request.headers["cf-access-jwt-assertion"];
114
+ return _effect.Option.fromNullable(token);
115
+ };
116
+ var authenticateRequest = (request, options) => {
117
+ return _effect.Effect.gen(function* () {
118
+ const token = extractToken(request);
119
+ const result = yield* _effect.Effect.tryPromise({
120
+ try: async () => {
121
+ return await _chunkWUJPWM4Tjs.validateCloudflareAccessToken.call(void 0,
122
+ _effect.Option.getOrUndefined(token),
123
+ {
124
+ accessConfig: options.accessConfig,
125
+ allowedEmails: options.allowedEmails,
126
+ skipInDev: options.skipInDev,
127
+ environment: options.environment
128
+ },
129
+ request.url
130
+ );
131
+ },
132
+ catch: (error) => {
133
+ if (_chunkWUJPWM4Tjs.isCloudflareAccessError.call(void 0, error)) {
134
+ return error;
135
+ }
136
+ return new (0, _chunkWUJPWM4Tjs.CloudflareAccessError)(
137
+ _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.INVALID_TOKEN,
138
+ error instanceof Error ? error.message : "Unknown error",
139
+ { context: { requestUrl: request.url } }
140
+ );
141
+ }
142
+ });
143
+ if (result instanceof _chunkWUJPWM4Tjs.CloudflareAccessError) {
144
+ return yield* _effect.Effect.fail(result);
145
+ }
146
+ if (!result.success) {
147
+ const errorCode = _nullishCoalesce(_optionalChain([result, 'access', _7 => _7.error, 'optionalAccess', _8 => _8.code]), () => ( _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.INVALID_TOKEN));
148
+ const errorMessage = _nullishCoalesce(_optionalChain([result, 'access', _9 => _9.error, 'optionalAccess', _10 => _10.message]), () => ( "Authentication failed"));
149
+ switch (errorCode) {
150
+ case _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.AUTH_REQUIRED:
151
+ return yield* _effect.Effect.fail(
152
+ new (0, _chunkWUJPWM4Tjs.AuthRequiredError)({
153
+ context: { requestUrl: request.url, ...result.error.context }
154
+ })
155
+ );
156
+ case _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.ACCESS_DENIED:
157
+ return yield* _effect.Effect.fail(
158
+ new (0, _chunkWUJPWM4Tjs.AccessDeniedError)(_nullishCoalesce(_optionalChain([result, 'access', _11 => _11.user, 'optionalAccess', _12 => _12.email]), () => ( "unknown")), {
159
+ requestUrl: request.url,
160
+ allowedEmails: options.allowedEmails
161
+ })
162
+ );
163
+ case _chunkWUJPWM4Tjs.CloudflareAccessErrorCode.INVALID_TOKEN:
164
+ return yield* _effect.Effect.fail(
165
+ new (0, _chunkWUJPWM4Tjs.InvalidTokenError)(errorMessage, {
166
+ requestUrl: request.url
167
+ })
168
+ );
169
+ default:
170
+ return yield* _effect.Effect.fail(
171
+ new (0, _chunkWUJPWM4Tjs.CloudflareAccessError)(errorCode, errorMessage, {
172
+ context: { requestUrl: request.url, ...result.error.context }
173
+ })
174
+ );
175
+ }
176
+ }
177
+ if (!result.user) {
178
+ return {
179
+ email: "dev@example.com",
180
+ userId: "dev-user",
181
+ country: "dev"
182
+ };
183
+ }
184
+ return result.user;
185
+ });
186
+ };
187
+ var getUser = (request, options) => {
188
+ return authenticateRequest(request, options).pipe(
189
+ _effect.Effect.map((user) => _effect.Option.some(user)),
190
+ _effect.Effect.catchAll(() => _effect.Effect.succeed(_effect.Option.none()))
191
+ );
192
+ };
193
+ var authenticateEither = (request, options) => {
194
+ return _effect.Effect.either(authenticateRequest(request, options));
195
+ };
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+ exports.AccessDeniedError = _chunkWUJPWM4Tjs.AccessDeniedError; exports.AuthRequiredError = _chunkWUJPWM4Tjs.AuthRequiredError; exports.CloudflareAccessAuth = CloudflareAccessAuth; exports.CloudflareAccessError = _chunkWUJPWM4Tjs.CloudflareAccessError; exports.CloudflareAccessErrorCode = _chunkWUJPWM4Tjs.CloudflareAccessErrorCode; exports.ConfigurationError = _chunkWUJPWM4Tjs.ConfigurationError; exports.CurrentUser = CurrentUser; exports.Forbidden = Forbidden; exports.InvalidTokenError = _chunkWUJPWM4Tjs.InvalidTokenError; exports.Unauthorized = Unauthorized; exports.__clearJwksCache = _chunkWUJPWM4Tjs.__clearJwksCache; exports.authenticateEither = authenticateEither; exports.authenticateRequest = authenticateRequest; exports.extractToken = extractToken; exports.getCloudflareAccessConfigFromEnv = _chunkWUJPWM4Tjs.getCloudflareAccessConfigFromEnv; exports.getUser = getUser; exports.isAccessDeniedError = _chunkWUJPWM4Tjs.isAccessDeniedError; exports.isAuthRequiredError = _chunkWUJPWM4Tjs.isAuthRequiredError; exports.isCloudflareAccessError = _chunkWUJPWM4Tjs.isCloudflareAccessError; exports.isConfigurationError = _chunkWUJPWM4Tjs.isConfigurationError; exports.isInvalidTokenError = _chunkWUJPWM4Tjs.isInvalidTokenError; exports.makeCloudflareAccessLive = makeCloudflareAccessLive; exports.toAuthError = _chunkWUJPWM4Tjs.toAuthError;
221
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/v000281/personal/hono-cloudflare-access-middleware/dist/adapters/effect/index.js","../../../src/adapters/effect/errors.ts","../../../src/adapters/effect/context.ts","../../../src/adapters/effect/middleware.ts","../../../src/adapters/effect/utils.ts"],"names":["Schema","Effect"],"mappings":"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","file":"/Users/v000281/personal/hono-cloudflare-access-middleware/dist/adapters/effect/index.js","sourcesContent":[null,"import { Schema } from \"effect\";\nimport { HttpApiSchema } from \"@effect/platform\";\n\n/**\n * Unauthorized error schema for Effect Platform\n */\nexport class Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {\n message: Schema.String,\n code: Schema.String,\n },\n HttpApiSchema.annotations({ status: 401 }),\n) {}\n\n/**\n * Forbidden error schema for Effect Platform\n */\nexport class Forbidden extends Schema.TaggedError<Forbidden>()(\n \"Forbidden\",\n {\n message: Schema.String,\n email: Schema.String,\n },\n HttpApiSchema.annotations({ status: 403 }),\n) {}\n","import { Context } from \"effect\";\nimport type { CloudflareAccessUser } from \"../../core/index\";\n\n/**\n * Context Tag for the authenticated user.\n * Use this to access the current user in your handlers.\n *\n * @example\n * ```typescript\n * import { Effect } from \"effect\";\n * import { CurrentUser } from \"cloudflare-access/effect\";\n *\n * const handler = Effect.gen(function* () {\n * const user = yield* CurrentUser;\n * console.log(`Hello ${user.email}`);\n * return user;\n * });\n * ```\n */\nexport class CurrentUser extends Context.Tag(\"cloudflare-access/CurrentUser\")<\n CurrentUser,\n CloudflareAccessUser\n>() {}\n","import { Effect, Layer, Redacted, Schema } from \"effect\";\nimport { HttpApiMiddleware, HttpApiSecurity, HttpServerRequest } from \"@effect/platform\";\nimport { validateCloudflareAccessToken, CloudflareAccessErrorCode } from \"../../core/index\";\nimport type { CloudflareAccessMiddlewareOptions } from \"./types\";\nimport { Unauthorized, Forbidden } from \"./errors\";\nimport { CurrentUser } from \"./context\";\n\n/**\n * Cloudflare Access authentication middleware class.\n *\n * Use this class directly in your API middleware definitions.\n *\n * @example\n * ```typescript\n * import { HttpApi, HttpApiGroup, HttpApiEndpoint } from \"@effect/platform\";\n * import { Schema, Effect, Layer } from \"effect\";\n * import {\n * CloudflareAccessAuth,\n * CurrentUser,\n * makeCloudflareAccessLive,\n * Unauthorized,\n * Forbidden\n * } from \"cloudflare-access/effect\";\n *\n * // Configure the middleware\n * const CloudflareAccessLive = makeCloudflareAccessLive({\n * accessConfig: {\n * teamDomain: \"https://yourteam.cloudflareaccess.com\",\n * audTag: \"your-audience-tag\",\n * },\n * allowedEmails: [\"admin@example.com\"],\n * skipInDev: true,\n * environment: \"dev\",\n * });\n *\n * // Define API with protected endpoints\n * const User = Schema.Struct({ email: Schema.String });\n *\n * const api = HttpApi.make(\"api\").add(\n * HttpApiGroup.make(\"users\").add(\n * HttpApiEndpoint.get(\"profile\", \"/profile\")\n * .addSuccess(User)\n * .middleware(CloudflareAccessAuth)\n * )\n * );\n *\n * // Implement handlers with access to CurrentUser\n * const UsersLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n * Effect.gen(function* () {\n * const user = yield* CurrentUser;\n * return handlers.handle(\"profile\", () =>\n * Effect.succeed({ email: user.email })\n * );\n * })\n * ).pipe(Layer.provide(CloudflareAccessLive));\n * ```\n */\nexport class CloudflareAccessAuth extends HttpApiMiddleware.Tag<CloudflareAccessAuth>()(\n \"CloudflareAccessAuth\",\n {\n provides: CurrentUser,\n failure: Schema.Union(Unauthorized, Forbidden),\n security: {\n bearer: HttpApiSecurity.bearer,\n },\n },\n) {}\n\n/**\n * Create a Layer that implements Cloudflare Access authentication.\n *\n * This layer validates the Cloudflare Access JWT token from the request\n * and provides the authenticated user via the CurrentUser context.\n *\n * @param options Configuration for Cloudflare Access\n * @returns Layer that provides authentication\n */\nexport const makeCloudflareAccessLive = (options: CloudflareAccessMiddlewareOptions) => {\n return Layer.succeed(\n CloudflareAccessAuth,\n CloudflareAccessAuth.of({\n bearer: (bearerToken) =>\n Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const token = Redacted.value(bearerToken);\n\n // Wrap the async validation in an Effect\n const result = yield* Effect.promise(() =>\n validateCloudflareAccessToken(\n token,\n {\n accessConfig: options.accessConfig,\n allowedEmails: options.allowedEmails,\n skipInDev: options.skipInDev,\n environment: options.environment,\n },\n request.url,\n ),\n );\n\n // Handle validation result\n if (!result.success) {\n const errorCode = result.error?.code ?? CloudflareAccessErrorCode.INVALID_TOKEN;\n const errorMessage = result.error?.message ?? \"Authentication failed\";\n\n if (errorCode === CloudflareAccessErrorCode.AUTH_REQUIRED) {\n return yield* Effect.fail(\n new Unauthorized({ message: errorMessage, code: \"AUTH_REQUIRED\" }),\n );\n }\n\n if (errorCode === CloudflareAccessErrorCode.ACCESS_DENIED) {\n return yield* Effect.fail(\n new Forbidden({\n message: errorMessage,\n email: result.user?.email ?? \"unknown\",\n }),\n );\n }\n\n return yield* Effect.fail(new Unauthorized({ message: errorMessage, code: errorCode }));\n }\n\n // Dev mode skip case (result.user can be null when skipped)\n if (!result.user) {\n return {\n email: \"dev@example.com\",\n userId: \"dev-user\",\n country: \"dev\",\n };\n }\n\n return result.user;\n }),\n }),\n );\n};\n","import { Effect, Option } from \"effect\";\nimport { HttpServerRequest } from \"@effect/platform\";\nimport {\n validateCloudflareAccessToken,\n CloudflareAccessError,\n CloudflareAccessErrorCode,\n AuthRequiredError,\n InvalidTokenError,\n AccessDeniedError,\n isCloudflareAccessError,\n type CloudflareAccessUser,\n} from \"../../core/index\";\nimport type { CloudflareAccessMiddlewareOptions } from \"./types\";\n\n/**\n * Extract Cloudflare Access token from request headers.\n * Looks for CF-Access-JWT-Assertion header.\n *\n * @param request HTTP server request\n * @returns Option with token if present\n */\nexport const extractToken = (\n request: HttpServerRequest.HttpServerRequest,\n): Option.Option<string> => {\n const token = request.headers[\"cf-access-jwt-assertion\"];\n return Option.fromNullable(token);\n};\n\n/**\n * Authenticate using the CF-Access-JWT-Assertion header directly.\n * This bypasses the HttpApiSecurity bearer token pattern.\n *\n * @param request HTTP server request\n * @param options Authentication options\n * @returns Effect with authenticated user or error\n */\nexport const authenticateRequest = (\n request: HttpServerRequest.HttpServerRequest,\n options: CloudflareAccessMiddlewareOptions,\n): Effect.Effect<CloudflareAccessUser, CloudflareAccessError, never> => {\n return Effect.gen(function* () {\n const token = extractToken(request);\n\n const result = yield* Effect.tryPromise({\n try: async () => {\n return await validateCloudflareAccessToken(\n Option.getOrUndefined(token),\n {\n accessConfig: options.accessConfig,\n allowedEmails: options.allowedEmails,\n skipInDev: options.skipInDev,\n environment: options.environment,\n },\n request.url,\n );\n },\n catch: (error) => {\n if (isCloudflareAccessError(error)) {\n return error;\n }\n return new CloudflareAccessError(\n CloudflareAccessErrorCode.INVALID_TOKEN,\n error instanceof Error ? error.message : \"Unknown error\",\n { context: { requestUrl: request.url } },\n );\n },\n });\n\n // Handle case where catch block returned a CloudflareAccessError directly\n if (result instanceof CloudflareAccessError) {\n return yield* Effect.fail(result);\n }\n\n if (!result.success) {\n const errorCode = result.error?.code ?? CloudflareAccessErrorCode.INVALID_TOKEN;\n const errorMessage = result.error?.message ?? \"Authentication failed\";\n\n switch (errorCode) {\n case CloudflareAccessErrorCode.AUTH_REQUIRED:\n return yield* Effect.fail(\n new AuthRequiredError({\n context: { requestUrl: request.url, ...result.error.context },\n }),\n );\n case CloudflareAccessErrorCode.ACCESS_DENIED:\n return yield* Effect.fail(\n new AccessDeniedError(result.user?.email ?? \"unknown\", {\n requestUrl: request.url,\n allowedEmails: options.allowedEmails,\n }),\n );\n case CloudflareAccessErrorCode.INVALID_TOKEN:\n return yield* Effect.fail(\n new InvalidTokenError(errorMessage, {\n requestUrl: request.url,\n }),\n );\n default:\n return yield* Effect.fail(\n new CloudflareAccessError(errorCode, errorMessage, {\n context: { requestUrl: request.url, ...result.error.context },\n }),\n );\n }\n }\n\n // Dev mode skip case\n if (!result.user) {\n return {\n email: \"dev@example.com\",\n userId: \"dev-user\",\n country: \"dev\",\n };\n }\n\n return result.user;\n });\n};\n\n/**\n * Get user as Option (returns None on auth failure)\n */\nexport const getUser = (\n request: HttpServerRequest.HttpServerRequest,\n options: CloudflareAccessMiddlewareOptions,\n): Effect.Effect<Option.Option<CloudflareAccessUser>, never, never> => {\n return authenticateRequest(request, options).pipe(\n Effect.map((user) => Option.some(user)),\n Effect.catchAll(() => Effect.succeed(Option.none<CloudflareAccessUser>())),\n );\n};\n\n/**\n * Authenticate and return Either (for explicit error handling)\n */\nexport const authenticateEither = (\n request: HttpServerRequest.HttpServerRequest,\n options: CloudflareAccessMiddlewareOptions,\n) => {\n return Effect.either(authenticateRequest(request, options));\n};\n"]}
@@ -0,0 +1,221 @@
1
+ import {
2
+ AccessDeniedError,
3
+ AuthRequiredError,
4
+ CloudflareAccessError,
5
+ CloudflareAccessErrorCode,
6
+ ConfigurationError,
7
+ InvalidTokenError,
8
+ __clearJwksCache,
9
+ getCloudflareAccessConfigFromEnv,
10
+ isAccessDeniedError,
11
+ isAuthRequiredError,
12
+ isCloudflareAccessError,
13
+ isConfigurationError,
14
+ isInvalidTokenError,
15
+ toAuthError,
16
+ validateCloudflareAccessToken
17
+ } from "../../chunk-DM2KGIQX.mjs";
18
+
19
+ // src/adapters/effect/errors.ts
20
+ import { Schema } from "effect";
21
+ import { HttpApiSchema } from "@effect/platform";
22
+ var Unauthorized = class extends Schema.TaggedError()(
23
+ "Unauthorized",
24
+ {
25
+ message: Schema.String,
26
+ code: Schema.String
27
+ },
28
+ HttpApiSchema.annotations({ status: 401 })
29
+ ) {
30
+ };
31
+ var Forbidden = class extends Schema.TaggedError()(
32
+ "Forbidden",
33
+ {
34
+ message: Schema.String,
35
+ email: Schema.String
36
+ },
37
+ HttpApiSchema.annotations({ status: 403 })
38
+ ) {
39
+ };
40
+
41
+ // src/adapters/effect/context.ts
42
+ import { Context } from "effect";
43
+ var CurrentUser = class extends Context.Tag("cloudflare-access/CurrentUser")() {
44
+ };
45
+
46
+ // src/adapters/effect/middleware.ts
47
+ import { Effect, Layer, Redacted, Schema as Schema2 } from "effect";
48
+ import { HttpApiMiddleware, HttpApiSecurity, HttpServerRequest } from "@effect/platform";
49
+ var CloudflareAccessAuth = class extends HttpApiMiddleware.Tag()(
50
+ "CloudflareAccessAuth",
51
+ {
52
+ provides: CurrentUser,
53
+ failure: Schema2.Union(Unauthorized, Forbidden),
54
+ security: {
55
+ bearer: HttpApiSecurity.bearer
56
+ }
57
+ }
58
+ ) {
59
+ };
60
+ var makeCloudflareAccessLive = (options) => {
61
+ return Layer.succeed(
62
+ CloudflareAccessAuth,
63
+ CloudflareAccessAuth.of({
64
+ bearer: (bearerToken) => Effect.gen(function* () {
65
+ const request = yield* HttpServerRequest.HttpServerRequest;
66
+ const token = Redacted.value(bearerToken);
67
+ const result = yield* Effect.promise(
68
+ () => validateCloudflareAccessToken(
69
+ token,
70
+ {
71
+ accessConfig: options.accessConfig,
72
+ allowedEmails: options.allowedEmails,
73
+ skipInDev: options.skipInDev,
74
+ environment: options.environment
75
+ },
76
+ request.url
77
+ )
78
+ );
79
+ if (!result.success) {
80
+ const errorCode = result.error?.code ?? CloudflareAccessErrorCode.INVALID_TOKEN;
81
+ const errorMessage = result.error?.message ?? "Authentication failed";
82
+ if (errorCode === CloudflareAccessErrorCode.AUTH_REQUIRED) {
83
+ return yield* Effect.fail(
84
+ new Unauthorized({ message: errorMessage, code: "AUTH_REQUIRED" })
85
+ );
86
+ }
87
+ if (errorCode === CloudflareAccessErrorCode.ACCESS_DENIED) {
88
+ return yield* Effect.fail(
89
+ new Forbidden({
90
+ message: errorMessage,
91
+ email: result.user?.email ?? "unknown"
92
+ })
93
+ );
94
+ }
95
+ return yield* Effect.fail(new Unauthorized({ message: errorMessage, code: errorCode }));
96
+ }
97
+ if (!result.user) {
98
+ return {
99
+ email: "dev@example.com",
100
+ userId: "dev-user",
101
+ country: "dev"
102
+ };
103
+ }
104
+ return result.user;
105
+ })
106
+ })
107
+ );
108
+ };
109
+
110
+ // src/adapters/effect/utils.ts
111
+ import { Effect as Effect2, Option } from "effect";
112
+ var extractToken = (request) => {
113
+ const token = request.headers["cf-access-jwt-assertion"];
114
+ return Option.fromNullable(token);
115
+ };
116
+ var authenticateRequest = (request, options) => {
117
+ return Effect2.gen(function* () {
118
+ const token = extractToken(request);
119
+ const result = yield* Effect2.tryPromise({
120
+ try: async () => {
121
+ return await validateCloudflareAccessToken(
122
+ Option.getOrUndefined(token),
123
+ {
124
+ accessConfig: options.accessConfig,
125
+ allowedEmails: options.allowedEmails,
126
+ skipInDev: options.skipInDev,
127
+ environment: options.environment
128
+ },
129
+ request.url
130
+ );
131
+ },
132
+ catch: (error) => {
133
+ if (isCloudflareAccessError(error)) {
134
+ return error;
135
+ }
136
+ return new CloudflareAccessError(
137
+ CloudflareAccessErrorCode.INVALID_TOKEN,
138
+ error instanceof Error ? error.message : "Unknown error",
139
+ { context: { requestUrl: request.url } }
140
+ );
141
+ }
142
+ });
143
+ if (result instanceof CloudflareAccessError) {
144
+ return yield* Effect2.fail(result);
145
+ }
146
+ if (!result.success) {
147
+ const errorCode = result.error?.code ?? CloudflareAccessErrorCode.INVALID_TOKEN;
148
+ const errorMessage = result.error?.message ?? "Authentication failed";
149
+ switch (errorCode) {
150
+ case CloudflareAccessErrorCode.AUTH_REQUIRED:
151
+ return yield* Effect2.fail(
152
+ new AuthRequiredError({
153
+ context: { requestUrl: request.url, ...result.error.context }
154
+ })
155
+ );
156
+ case CloudflareAccessErrorCode.ACCESS_DENIED:
157
+ return yield* Effect2.fail(
158
+ new AccessDeniedError(result.user?.email ?? "unknown", {
159
+ requestUrl: request.url,
160
+ allowedEmails: options.allowedEmails
161
+ })
162
+ );
163
+ case CloudflareAccessErrorCode.INVALID_TOKEN:
164
+ return yield* Effect2.fail(
165
+ new InvalidTokenError(errorMessage, {
166
+ requestUrl: request.url
167
+ })
168
+ );
169
+ default:
170
+ return yield* Effect2.fail(
171
+ new CloudflareAccessError(errorCode, errorMessage, {
172
+ context: { requestUrl: request.url, ...result.error.context }
173
+ })
174
+ );
175
+ }
176
+ }
177
+ if (!result.user) {
178
+ return {
179
+ email: "dev@example.com",
180
+ userId: "dev-user",
181
+ country: "dev"
182
+ };
183
+ }
184
+ return result.user;
185
+ });
186
+ };
187
+ var getUser = (request, options) => {
188
+ return authenticateRequest(request, options).pipe(
189
+ Effect2.map((user) => Option.some(user)),
190
+ Effect2.catchAll(() => Effect2.succeed(Option.none()))
191
+ );
192
+ };
193
+ var authenticateEither = (request, options) => {
194
+ return Effect2.either(authenticateRequest(request, options));
195
+ };
196
+ export {
197
+ AccessDeniedError,
198
+ AuthRequiredError,
199
+ CloudflareAccessAuth,
200
+ CloudflareAccessError,
201
+ CloudflareAccessErrorCode,
202
+ ConfigurationError,
203
+ CurrentUser,
204
+ Forbidden,
205
+ InvalidTokenError,
206
+ Unauthorized,
207
+ __clearJwksCache,
208
+ authenticateEither,
209
+ authenticateRequest,
210
+ extractToken,
211
+ getCloudflareAccessConfigFromEnv,
212
+ getUser,
213
+ isAccessDeniedError,
214
+ isAuthRequiredError,
215
+ isCloudflareAccessError,
216
+ isConfigurationError,
217
+ isInvalidTokenError,
218
+ makeCloudflareAccessLive,
219
+ toAuthError
220
+ };
221
+ //# sourceMappingURL=index.mjs.map