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,85 @@
1
+ /**
2
+ * Custom Error Handlers Example
3
+ *
4
+ * Customize the response when authentication fails or access is denied.
5
+ */
6
+
7
+ import { Hono } from "hono";
8
+ import {
9
+ createCloudflareAccessAuth,
10
+ getCloudflareAccessConfigFromBindings,
11
+ type CloudflareAccessUser,
12
+ } from "cloudflare-access/hono";
13
+
14
+ interface Bindings {
15
+ CF_ACCESS_TEAM_DOMAIN: string;
16
+ CF_ACCESS_AUD: string;
17
+ ENVIRONMENT: string;
18
+ }
19
+
20
+ interface Variables {
21
+ user?: CloudflareAccessUser;
22
+ }
23
+
24
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
25
+
26
+ app.use(
27
+ createCloudflareAccessAuth({
28
+ accessConfig: getCloudflareAccessConfigFromBindings,
29
+ allowedEmails: ["admin@example.com"],
30
+
31
+ // Custom unauthorized response
32
+ onUnauthorized: (c, reason) => {
33
+ return c.json(
34
+ {
35
+ error: "Authentication Required",
36
+ message: "You must sign in to access this resource",
37
+ reason,
38
+ documentation: "https://docs.example.com/auth",
39
+ support: "support@example.com",
40
+ },
41
+ 401,
42
+ );
43
+ },
44
+
45
+ // Custom forbidden response
46
+ onForbidden: (c, email) => {
47
+ return c.json(
48
+ {
49
+ error: "Access Denied",
50
+ message: `Sorry ${email}, you don't have permission to access this resource`,
51
+ requiredRole: "admin",
52
+ currentUser: email,
53
+ upgradeRequestUrl: "https://example.com/request-access",
54
+ },
55
+ 403,
56
+ );
57
+ },
58
+
59
+ // Skip auth for these paths
60
+ excludePaths: ["/api/public", "/health", "/docs"],
61
+ }),
62
+ );
63
+
64
+ // Public routes (no auth required)
65
+ app.get("/api/public", (c) => {
66
+ return c.json({
67
+ message: "This is public data",
68
+ timestamp: new Date().toISOString(),
69
+ });
70
+ });
71
+
72
+ app.get("/health", (c) => {
73
+ return c.json({ status: "healthy" });
74
+ });
75
+
76
+ // Protected routes
77
+ app.get("/api/private", (c) => {
78
+ const user = c.get("user");
79
+ return c.json({
80
+ message: "Private data",
81
+ accessedBy: user?.email,
82
+ });
83
+ });
84
+
85
+ export default app;
@@ -0,0 +1,205 @@
1
+ import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "@effect/platform";
2
+ import { NodeHttpServer, NodeRuntime } from "@effect/platform-node";
3
+ import { Effect, Layer, Schema } from "effect";
4
+ import { createServer } from "node:http";
5
+ import {
6
+ CloudflareAccessAuth,
7
+ makeCloudflareAccessLive,
8
+ CurrentUser,
9
+ Unauthorized,
10
+ Forbidden,
11
+ } from "cloudflare-access/effect";
12
+
13
+ // ============================================================================
14
+ // 1. SCHEMAS
15
+ // ============================================================================
16
+
17
+ /**
18
+ * Schema for authenticated user
19
+ */
20
+ const User = Schema.Struct({
21
+ email: Schema.String,
22
+ userId: Schema.optional(Schema.String),
23
+ country: Schema.optional(Schema.String),
24
+ });
25
+
26
+ /**
27
+ * Schema for user profile response
28
+ */
29
+ const UserProfile = Schema.Struct({
30
+ email: Schema.String,
31
+ userId: Schema.optional(Schema.String),
32
+ country: Schema.optional(Schema.String),
33
+ preferences: Schema.Struct({
34
+ theme: Schema.String,
35
+ notifications: Schema.Boolean,
36
+ }),
37
+ lastLogin: Schema.String,
38
+ });
39
+
40
+ /**
41
+ * Schema for admin dashboard response
42
+ */
43
+ const AdminStats = Schema.Struct({
44
+ totalUsers: Schema.Number,
45
+ activeSessions: Schema.Number,
46
+ apiRequests: Schema.Number,
47
+ recentActivity: Schema.Array(Schema.String),
48
+ });
49
+
50
+ // ============================================================================
51
+ // 2. API DEFINITION
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Users API Group - Protected endpoints
56
+ */
57
+ const UsersGroup = HttpApiGroup.make("users")
58
+ .add(
59
+ HttpApiEndpoint.get("getProfile", "/users/me")
60
+ .addSuccess(UserProfile)
61
+ .addError(Unauthorized, { status: 401 })
62
+ .addError(Forbidden, { status: 403 }),
63
+ )
64
+ .middleware(CloudflareAccessAuth);
65
+
66
+ /**
67
+ * Admin API Group - Admin-only endpoints
68
+ */
69
+ const AdminGroup = HttpApiGroup.make("admin")
70
+ .add(
71
+ HttpApiEndpoint.get("getStats", "/admin/stats")
72
+ .addSuccess(AdminStats)
73
+ .addError(Unauthorized, { status: 401 })
74
+ .addError(Forbidden, { status: 403 }),
75
+ )
76
+ .add(
77
+ HttpApiEndpoint.get("getUsers", "/admin/users")
78
+ .addSuccess(Schema.Array(User))
79
+ .addError(Unauthorized, { status: 401 })
80
+ .addError(Forbidden, { status: 403 }),
81
+ )
82
+ .middleware(CloudflareAccessAuth);
83
+
84
+ /**
85
+ * Public API Group - No authentication required
86
+ */
87
+ const PublicGroup = HttpApiGroup.make("public").add(
88
+ HttpApiEndpoint.get("health", "/health").addSuccess(Schema.Struct({ status: Schema.String })),
89
+ );
90
+
91
+ /**
92
+ * Main API definition
93
+ */
94
+ const Api = HttpApi.make("CloudflareAccessApi").add(PublicGroup).add(UsersGroup).add(AdminGroup);
95
+
96
+ // ============================================================================
97
+ // 3. IMPLEMENTATION
98
+ // ============================================================================
99
+
100
+ /**
101
+ * Authentication layer - provides the middleware implementation
102
+ */
103
+ const CloudflareAccessLive = makeCloudflareAccessLive({
104
+ accessConfig: {
105
+ teamDomain: "https://yourteam.cloudflareaccess.com",
106
+ audTag: "your-audience-tag",
107
+ },
108
+ allowedEmails: ["admin@example.com", "user@example.com"],
109
+ skipInDev: true,
110
+ environment: "dev",
111
+ });
112
+
113
+ /**
114
+ * Implement the Users group
115
+ */
116
+ const UsersLive = HttpApiBuilder.group(Api, "users", (handlers) =>
117
+ handlers.handle("getProfile", () =>
118
+ Effect.gen(function* () {
119
+ const user = yield* CurrentUser;
120
+ return {
121
+ email: user.email,
122
+ userId: user.userId,
123
+ country: user.country,
124
+ preferences: {
125
+ theme: "dark",
126
+ notifications: true,
127
+ },
128
+ lastLogin: new Date().toISOString(),
129
+ };
130
+ }),
131
+ ),
132
+ ).pipe(Layer.provide(CloudflareAccessLive));
133
+
134
+ /**
135
+ * Implement the Admin group with admin-only access
136
+ */
137
+ const AdminLive = HttpApiBuilder.group(Api, "admin", (handlers) =>
138
+ handlers
139
+ .handle("getStats", () =>
140
+ Effect.gen(function* () {
141
+ const user = yield* CurrentUser;
142
+ const allowedAdmins = ["admin@example.com"];
143
+
144
+ if (!allowedAdmins.includes(user.email)) {
145
+ return yield* Effect.fail(
146
+ new Forbidden({
147
+ message: "Admin access required",
148
+ email: user.email,
149
+ }),
150
+ );
151
+ }
152
+
153
+ return {
154
+ totalUsers: 1500,
155
+ activeSessions: 342,
156
+ apiRequests: 125000,
157
+ recentActivity: ["User login", "Config update"],
158
+ };
159
+ }),
160
+ )
161
+ .handle("getUsers", () =>
162
+ Effect.gen(function* () {
163
+ const user = yield* CurrentUser;
164
+ const allowedAdmins = ["admin@example.com"];
165
+
166
+ if (!allowedAdmins.includes(user.email)) {
167
+ return yield* Effect.fail(
168
+ new Forbidden({
169
+ message: "Admin access required",
170
+ email: user.email,
171
+ }),
172
+ );
173
+ }
174
+
175
+ return [
176
+ { email: "user1@example.com", userId: "1" },
177
+ { email: "user2@example.com", userId: "2" },
178
+ ];
179
+ }),
180
+ ),
181
+ ).pipe(Layer.provide(CloudflareAccessLive));
182
+
183
+ /**
184
+ * Implement the Public group (no auth required)
185
+ */
186
+ const PublicLive = HttpApiBuilder.group(Api, "public", (handlers) =>
187
+ handlers.handle("health", () => Effect.succeed({ status: "healthy" })),
188
+ );
189
+
190
+ // ============================================================================
191
+ // 4. SERVER SETUP
192
+ // ============================================================================
193
+
194
+ const ApiLive = HttpApiBuilder.api(Api).pipe(
195
+ Layer.provide(UsersLive),
196
+ Layer.provide(AdminLive),
197
+ Layer.provide(PublicLive),
198
+ );
199
+
200
+ HttpApiBuilder.serve().pipe(
201
+ Layer.provide(ApiLive),
202
+ Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
203
+ Layer.launch,
204
+ NodeRuntime.runMain,
205
+ );
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Email Allowlist Example
3
+ *
4
+ * Restrict access to specific email addresses. This provides an additional
5
+ * layer of security beyond Cloudflare Access policies.
6
+ */
7
+
8
+ import { Hono } from "hono";
9
+ import {
10
+ createCloudflareAccessAuth,
11
+ getCloudflareAccessConfigFromBindings,
12
+ type CloudflareAccessUser,
13
+ } from "cloudflare-access/hono";
14
+
15
+ interface Bindings {
16
+ CF_ACCESS_TEAM_DOMAIN: string;
17
+ CF_ACCESS_AUD: string;
18
+ }
19
+
20
+ interface Variables {
21
+ user?: CloudflareAccessUser;
22
+ }
23
+
24
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
25
+
26
+ // Only allow specific emails
27
+ const ALLOWED_EMAILS = ["admin@company.com", "ceo@company.com", "cto@company.com"];
28
+
29
+ app.use(
30
+ createCloudflareAccessAuth({
31
+ accessConfig: getCloudflareAccessConfigFromBindings,
32
+ allowedEmails: ALLOWED_EMAILS,
33
+ }),
34
+ );
35
+
36
+ // Only accessible to allowed emails
37
+ app.get("/api/sensitive-data", (c) => {
38
+ const user = c.get("user");
39
+
40
+ return c.json({
41
+ message: "Access granted to sensitive data",
42
+ user: user?.email,
43
+ data: {
44
+ revenue: 1000000,
45
+ projections: "classified",
46
+ },
47
+ });
48
+ });
49
+
50
+ export default app;
@@ -0,0 +1,26 @@
1
+ import express from "express";
2
+ import { cloudflareAccessAuth } from "cloudflare-access/express";
3
+
4
+ const app = express();
5
+
6
+ // Use Cloudflare Access middleware
7
+ app.use(
8
+ cloudflareAccessAuth({
9
+ accessConfig: {
10
+ teamDomain: "https://yourteam.cloudflareaccess.com",
11
+ audTag: "your-audience-tag",
12
+ },
13
+ }),
14
+ );
15
+
16
+ app.get("/protected", (req, res) => {
17
+ res.json({
18
+ message: `Hello ${req.user?.email}`,
19
+ userId: req.user?.userId,
20
+ country: req.user?.country,
21
+ });
22
+ });
23
+
24
+ app.listen(3000, () => {
25
+ console.log("Server running on http://localhost:3000");
26
+ });
@@ -0,0 +1,24 @@
1
+ import fastify from "fastify";
2
+ import { cloudflareAccessPlugin } from "cloudflare-access/fastify";
3
+
4
+ const app = fastify();
5
+
6
+ // Register Cloudflare Access plugin
7
+ app.register(cloudflareAccessPlugin, {
8
+ accessConfig: {
9
+ teamDomain: "https://yourteam.cloudflareaccess.com",
10
+ audTag: "your-audience-tag",
11
+ },
12
+ });
13
+
14
+ app.get("/protected", async (request, _reply) => {
15
+ return {
16
+ message: `Hello ${request.user?.email}`,
17
+ userId: request.user?.userId,
18
+ country: request.user?.country,
19
+ };
20
+ });
21
+
22
+ app.listen({ port: 3000 }, () => {
23
+ console.log("Server running on http://localhost:3000");
24
+ });
@@ -0,0 +1,26 @@
1
+ import { Hono } from "hono";
2
+ import {
3
+ createCloudflareAccessAuth,
4
+ getCloudflareAccessConfigFromBindings,
5
+ type CloudflareAccessVariables,
6
+ } from "cloudflare-access/hono";
7
+
8
+ const app = new Hono<{ Variables: CloudflareAccessVariables }>();
9
+
10
+ // Use with Cloudflare Access bindings
11
+ app.use(
12
+ createCloudflareAccessAuth({
13
+ accessConfig: getCloudflareAccessConfigFromBindings,
14
+ }),
15
+ );
16
+
17
+ app.get("/protected", (c) => {
18
+ const user = c.get("user");
19
+ return c.json({
20
+ message: `Hello ${user?.email}`,
21
+ userId: user?.userId,
22
+ country: user?.country,
23
+ });
24
+ });
25
+
26
+ export default app;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Hono Router Example
3
+ *
4
+ * Using the middleware with Hono's router pattern for modular APIs.
5
+ */
6
+
7
+ import { Hono } from "hono";
8
+ import {
9
+ createCloudflareAccessAuth,
10
+ getCloudflareAccessConfigFromBindings,
11
+ type CloudflareAccessUser,
12
+ } from "cloudflare-access/hono";
13
+
14
+ interface Bindings {
15
+ CF_ACCESS_TEAM_DOMAIN: string;
16
+ CF_ACCESS_AUD: string;
17
+ }
18
+
19
+ // Extend Hono's variables type
20
+ interface Variables {
21
+ user?: CloudflareAccessUser;
22
+ }
23
+
24
+ // Create typed Hono app
25
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
26
+
27
+ // API v1 routes
28
+ const apiV1 = new Hono<{ Bindings: Bindings; Variables: Variables }>();
29
+
30
+ apiV1.use(
31
+ createCloudflareAccessAuth({
32
+ accessConfig: getCloudflareAccessConfigFromBindings,
33
+ }),
34
+ );
35
+
36
+ apiV1.get("/users/me", (c) => {
37
+ const user = c.get("user");
38
+ return c.json({
39
+ id: user?.userId,
40
+ email: user?.email,
41
+ country: user?.country,
42
+ });
43
+ });
44
+
45
+ apiV1.get("/dashboard", (c) => {
46
+ const user = c.get("user");
47
+ return c.json({
48
+ welcome: `Hello ${user?.email}`,
49
+ lastLogin: new Date().toISOString(),
50
+ });
51
+ });
52
+
53
+ // Public routes
54
+ const publicRouter = new Hono();
55
+
56
+ publicRouter.get("/status", (c) => {
57
+ return c.json({
58
+ status: "operational",
59
+ version: "1.0.0",
60
+ });
61
+ });
62
+
63
+ // Mount routers
64
+ app.route("/api/v1", apiV1);
65
+ app.route("/public", publicRouter);
66
+
67
+ export default app;
68
+
69
+ /*
70
+ Route structure:
71
+ - GET /api/v1/users/me -> Protected (requires auth)
72
+ - GET /api/v1/dashboard -> Protected (requires auth)
73
+ - GET /public/status -> Public (no auth)
74
+ */
@@ -0,0 +1,39 @@
1
+ import { Controller, Get, Module, Req } from "@nestjs/common";
2
+ import type { Request } from "express";
3
+ import { CloudflareAccessGuard, Public } from "cloudflare-access/nestjs";
4
+ import { APP_GUARD } from "@nestjs/core";
5
+
6
+ @Controller("api")
7
+ export class ApiController {
8
+ @Get("protected")
9
+ getProtected(@Req() req: Request) {
10
+ return {
11
+ message: `Hello ${req.user?.email}`,
12
+ userId: req.user?.userId,
13
+ country: req.user?.country,
14
+ };
15
+ }
16
+
17
+ @Public()
18
+ @Get("public")
19
+ getPublic() {
20
+ return { message: "This is a public endpoint" };
21
+ }
22
+ }
23
+
24
+ @Module({
25
+ controllers: [ApiController],
26
+ providers: [
27
+ {
28
+ provide: APP_GUARD,
29
+ useFactory: () =>
30
+ new CloudflareAccessGuard({
31
+ accessConfig: {
32
+ teamDomain: "https://yourteam.cloudflareaccess.com",
33
+ audTag: "your-audience-tag",
34
+ },
35
+ }),
36
+ },
37
+ ],
38
+ })
39
+ export class AppModule {}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Skip Dev Mode Example
3
+ *
4
+ * Skip authentication during local development while still
5
+ * requiring it in production.
6
+ */
7
+
8
+ import { Hono } from "hono";
9
+ import {
10
+ createCloudflareAccessAuth,
11
+ getCloudflareAccessConfigFromBindings,
12
+ type CloudflareAccessUser,
13
+ } from "cloudflare-access/hono";
14
+
15
+ interface Bindings {
16
+ CF_ACCESS_TEAM_DOMAIN: string;
17
+ CF_ACCESS_AUD: string;
18
+ ENVIRONMENT: "dev" | "staging" | "prod";
19
+ }
20
+
21
+ interface Variables {
22
+ user?: CloudflareAccessUser;
23
+ }
24
+
25
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
26
+
27
+ app.use(
28
+ createCloudflareAccessAuth({
29
+ accessConfig: getCloudflareAccessConfigFromBindings,
30
+
31
+ // When true, auth is skipped for localhost/127.0.0.1
32
+ // when ENVIRONMENT is not "prod"
33
+ skipInDev: true,
34
+
35
+ // Also exclude specific paths from auth
36
+ excludePaths: ["/api/public", "/health"],
37
+ }),
38
+ );
39
+
40
+ // This route:
41
+ // - Requires auth in production
42
+ // - Skips auth on localhost in dev/staging
43
+ app.get("/api/private", (c) => {
44
+ const user = c.get("user");
45
+ const env = c.env.ENVIRONMENT;
46
+
47
+ // In dev mode, user might be undefined since auth is skipped
48
+ if (!user) {
49
+ return c.json({
50
+ message: "Development mode - auth skipped",
51
+ environment: env,
52
+ note: "In production, this would require authentication",
53
+ });
54
+ }
55
+
56
+ return c.json({
57
+ message: "Production mode - authenticated",
58
+ environment: env,
59
+ user: user?.email,
60
+ });
61
+ });
62
+
63
+ // Always public
64
+ app.get("/health", (c) => {
65
+ return c.json({ status: "ok" });
66
+ });
67
+
68
+ export default app;
69
+
70
+ /*
71
+ ## Development workflow:
72
+
73
+ 1. Local development (no auth required):
74
+ ```bash
75
+ ENVIRONMENT=dev bun run dev
76
+ curl http://localhost:8787/api/private
77
+ # Returns: { message: "Development mode..." }
78
+ ```
79
+
80
+ 2. Production (auth required):
81
+ ```bash
82
+ # Deployed to Cloudflare with ENVIRONMENT=prod
83
+ curl https://api.example.com/api/private
84
+ # Returns: 401 Unauthorized
85
+
86
+ curl -H "CF-Access-JWT-Assertion: <token>" https://api.example.com/api/private
87
+ # Returns: { message: "Production mode...", user: "..." }
88
+ ```
89
+ */