Skip to content

Commit aa23c33

Browse files
PCX-12892 - initial version of the tutorial
1 parent 09ec545 commit aa23c33

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed
45.4 KB
Loading
36.7 KB
Loading
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
---
2+
updated: 2024-08-13
3+
category: 🔐 Zero Trust
4+
difficulty: Advanced
5+
pcx_content_type: tutorial
6+
content_type: 📝 Tutorial
7+
title: Augment Clouflare Access SSO capabilities with Cloudflare Workers
8+
---
9+
10+
import { Render, GlossaryTooltip } from "~/components"
11+
12+
This tutorial will walk you through extending the single-sign-on (SSO) capabilities of [Cloudflare Access](/cloudflare-one/policies/access/) with Serverless using [Cloudflare Workers](/workers/). Specifically, this guide will demonstrate how to modify requests sent to your secured origin to include additional information from the Cloudflare Access authentication event.
13+
14+
Time to complete: **45 minutes**
15+
16+
## Authentication flow
17+
18+
[Cloudflare Access](/cloudflare-one/policies/access/) is an authentication proxy in charge of validating a user's identity before they connect to your application. As shown in the diagram below, Access inserts a [JWT](/cloudflare-one/identity/authorization-cookie/application-token/) into the request, which can then be [verified](/cloudflare-one/identity/authorization-cookie/validating-json/#validate-jwts) by the origin server.
19+
20+
![extendedflow](~/assets/images/cloudflare-one/applications/access-standard-flow.png)
21+
22+
You can extend this functionality by using a [Cloudflare Worker](/workers/) to insert additional HTTP headers into the request. In this example, we will add the [device posture attributes](/cloudflare-one/identity/devices/#enforce-device-posture) `firewall_activated` and `disk_encrypted`, but you can include any attributes that Cloudflare Access collects from the authentication event.
23+
24+
![standardflow](~/assets/images/cloudflare-one/applications/access-extended-flow-serverless.png)
25+
26+
## Benefits
27+
28+
This approach allows you to:
29+
30+
* **Enhance security:** By incorporating additional information from the authentication event, you can implement more robust security measures. For example, you can use device posture data to enforce access based on device compliance.
31+
* **Improve user experience:** You can personalize the user experience by tailoring content or functionality based on user attributes. For example, you can display different content based on the user's role or location.
32+
* **Simplify development:** By using Cloudflare Workers, you can easily extend your Cloudflare Access configuration without modifying your origin application code.
33+
34+
35+
## Before you begin
36+
37+
- Add a [self-hosted application](/cloudflare-one/applications/configure-apps/self-hosted-apps/) to Cloudflare Access.
38+
- Enable the [Disk encryption](/cloudflare-one/identity/devices/warp-client-checks/disk-encryption/) and [Firewall](/cloudflare-one/identity/devices/warp-client-checks/firewall/) device posture checks.
39+
- Install [Wrangler](/workers/wrangler/install-and-update/) on your local machine.
40+
41+
## 1. Create the Worker
42+
43+
1. Create a new Workers project:
44+
45+
```sh
46+
npm create cloudflare@latest -- device-posture-worker
47+
```
48+
49+
<Render file="c3-post-run-steps" product="workers" params={{ one: "Hello World example", two: "Hello World Worker", three: "JavaScript" }} />
50+
51+
2. Change to the project directory:
52+
53+
```sh
54+
$ cd device-posture-worker
55+
```
56+
57+
3. Copy-paste the following code into `src/index.js`. Be sure to replace `<your-team-name>` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
58+
59+
```js title="index.js"
60+
61+
import { parse } from "cookie";
62+
export default {
63+
async fetch(request, env, ctx) {
64+
// The name of the cookie
65+
const COOKIE_NAME = "CF_Authorization";
66+
const CF_GET_IDENTITY = "https://<your-team-name>.cloudflareaccess.com>/cdn-cgi/access/get-identity";
67+
const cookie = parse(request.headers.get("Cookie") || "");
68+
if (cookie[COOKIE_NAME] != null) {
69+
try {
70+
let id = await (await fetch(CF_GET_IDENTITY, request)).json()
71+
let diskEncryptionStatus = false;
72+
let firewallStatus = false;
73+
74+
for (const checkId in id.devicePosture) {
75+
const check = id.devicePosture[checkId];
76+
if (check.type === "disk_encryption") {
77+
console.log(check.type)
78+
diskEncryptionStatus = check.success;
79+
}
80+
if (check.type === "firewall") {
81+
console.log(check.type)
82+
firewallStatus = check.success;
83+
break;
84+
}
85+
}
86+
//clone request (immutable otherwise) and insert posture values in new header set
87+
let newRequest = await new Request(request)
88+
newRequest.headers.set("Cf-Access-Firewall-Activated", firewallStatus)
89+
newRequest.headers.set("Cf-Access-Disk-Encrypted", firewallStatus)
90+
91+
//sent modified request to origin
92+
return await fetch(newRequest)
93+
94+
} catch (e) {
95+
console.log(e)
96+
return await fetch(request)
97+
}
98+
}
99+
return await fetch(request)
100+
},
101+
};
102+
103+
```
104+
105+
## 2. View the user's identity
106+
107+
The script in `index.js` uses the [`get-identity`](/cloudflare-one/identity/authorization-cookie/application-token/#user-identity) endpoint to fetch a user's complete identity from a Cloudflare Access authentication event. To view a list of available data fields, log in to your Access application and append `/cdn-cgi/access/get-identity` to the URL. For example, if `www.example.com` is behind Access, go to `https://www.example.com/cdn-cgi/access/get-identity`.
108+
109+
Below is an example of a user identity that includes the `disk_encryption` and `firewall` posture checks. The Worker inserts the posture check results into the request headers **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted**.
110+
111+
```json title="Example user identity" {33,52}
112+
{
113+
"id": "P51Tuu01fWHMBjIBvrCK1lK-eUDWs2aQMv03WDqT5oY",
114+
"name": "John Doe",
115+
"email": "[email protected]",
116+
"amr": [
117+
"pwd"
118+
],
119+
"oidc_fields": {
120+
"principalName": "XXXXXX_cloudflare.com#EXT#@XXXXXXcloudflare.onmicrosoft.com"
121+
},
122+
"groups": [
123+
{
124+
"id": "fdaedb59-e9be-4ab7-8001-3e069da54185",
125+
"name": "XXXXX"
126+
}
127+
],
128+
"idp": {
129+
"id": "b9f4d68e-dac1-48b0-b728-ae05a5f0d4b2",
130+
"type": "azureAD"
131+
},
132+
"geo": {
133+
"country": "FR"
134+
},
135+
"user_uuid": "ce40d564-c72f-475f-a9b8-f395f19ad986",
136+
"account_id": "121287a0c6e6260ec930655e6b39a3a8",
137+
"iat": 1724056537,
138+
"devicePosture": {
139+
"f6f9391e-6776-4878-9c60-0cc807dc7dc8": {
140+
"id": "f6f9391e-6776-4878-9c60-0cc807dc7dc8",
141+
"schedule": "5m",
142+
"timestamp": "2024-08-19T08:31:59.274Z",
143+
"description": "",
144+
"type": "disk_encryption",
145+
"check": {
146+
"drives": {
147+
"C": {
148+
"encrypted": true
149+
}
150+
}
151+
},
152+
"success": false,
153+
"rule_name": "Disk Encryption - Windows",
154+
"input": {
155+
"requireAll": true,
156+
"checkDisks": []
157+
},
158+
"a0a8e83d-be75-4aa6-bfa0-5791da6e9186": {
159+
"id": "a0a8e83d-be75-4aa6-bfa0-5791da6e9186",
160+
"schedule": "5m",
161+
"timestamp": "2024-08-19T08:31:59.274Z",
162+
"description": "",
163+
"type": "firewall",
164+
"check": {
165+
"firewall": false
166+
},
167+
"success": false,
168+
"rule_name": "Local Firewall Check - Windows",
169+
"input": {
170+
"enabled": true
171+
}
172+
}
173+
...
174+
}
175+
```
176+
177+
## 3. Route the Worker to your application
178+
179+
In `wrangler.toml`, [set up a route](/workers/configuration/routing/routes/) that maps the Worker to your Access application domain:
180+
```toml
181+
route = { pattern= "app.example.com/*", zone_name="example.com"}
182+
```
183+
184+
## 4. Deploy the Worker
185+
186+
```sh
187+
npx wrangler deploy
188+
```
189+
190+
The Worker will now insert the **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted** headers into requests that pass your application's Access policies.
191+
192+
```json title="Example request headers" {7,8}
193+
{
194+
"headers": {
195+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
196+
"Accept-Encoding": "gzip",
197+
"Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-GB;q=0.6",
198+
"Cf-Access-Authenticated-User-Email": "[email protected]",
199+
"Cf-Access-Disk-Encrypted": "false",
200+
"Cf-Access-Firewall-Activated": "false",
201+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
202+
}
203+
}
204+
```
205+
206+
You can verify that these headers are received by the origin server.

0 commit comments

Comments
 (0)