This tutorial will walk you through extending the single-sign-on (SSO) capabilities of [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/policies/access/) with our serverless computing platform, [Cloudflare Workers](https://developers.cloudflare.com/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.

**Time to complete:** 45 minutes

## Authentication flow

[Cloudflare Access](https://developers.cloudflare.com/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](https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/application-token/) into the request, which can then be [verified](https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/#validate-jwts) by the origin server.

![Standard authentication flow for a request to an Access application](https://developers.cloudflare.com/_astro/access-standard-flow.CLZ6SIBs_Z1DXHBY.webp)

You can extend this functionality by using a Cloudflare Worker to insert additional HTTP headers into the request. In this example, we will add the [device posture attributes](https://developers.cloudflare.com/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.

![Extended authentication flow uses a Worker to pass additional request headers to the origin](https://developers.cloudflare.com/_astro/access-extended-flow-serverless.DKpY2r43_Z1G09wT.webp)

## Benefits

This approach allows you to:

* **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.
* **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.
* **Simplify development:** By using Cloudflare Workers, you can easily extend your Cloudflare Access configuration without modifying your origin application code.

## Before you begin

* Add a [self-hosted application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/) to Cloudflare Access.
* Enable the [Disk encryption](https://developers.cloudflare.com/cloudflare-one/identity/devices/warp-client-checks/disk-encryption/) and [Firewall](https://developers.cloudflare.com/cloudflare-one/identity/devices/warp-client-checks/firewall/) device posture checks.
* Install [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/) on your local machine.

## 1. Create the Worker

1. Create a new Workers project:

   * npm

     ```sh
     npm create cloudflare@latest -- device-posture-worker
     ```

   * pnpm

     ```sh
     pnpm create cloudflare@latest device-posture-worker
     ```

   * yarn

     ```sh
     yarn create cloudflare device-posture-worker
     ```

   For setup, select the following options:

   * For *What would you like to start with?*, choose `Hello World Starter`.
   * For *Which template would you like to use?*, choose `Worker only`.
   * For *Which language do you want to use?*, choose `JavaScript`.
   * For *Do you want to use git for version control?*, choose `Yes`.
   * For *Do you want to deploy your application?*, choose `No` (we will be making some changes before deploying).

2. Change to the project directory:

   ```sh
   $ cd device-posture-worker
   ```

3. Copy-paste the following code into `src/index.js`. Be sure to replace `<your-team-name>` with your Zero Trust team name.

   ```js
   import { parse } from "cookie";
   export default {
     async fetch(request, env, ctx) {
       // The name of the cookie
       const COOKIE_NAME = "CF_Authorization";
     const CF_GET_IDENTITY = "https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/get-identity";
       const cookie = parse(request.headers.get("Cookie") || "");
       if (cookie[COOKIE_NAME] != null) {
       try {
       let id = await (await fetch(CF_GET_IDENTITY, request)).json()
       let diskEncryptionStatus = false;
       let firewallStatus = false;


       for (const checkId in id.devicePosture) {
         const check = id.devicePosture[checkId];
         if (check.type === "disk_encryption") {
           console.log(check.type)
           diskEncryptionStatus = check.success;
         }
         if (check.type === "firewall") {
           console.log(check.type)
           firewallStatus = check.success;
           break;
         }
       }
       //clone request (immutable otherwise) and insert posture values in new header set
       let newRequest = await new Request(request)
       newRequest.headers.set("Cf-Access-Firewall-Activated", firewallStatus)
       newRequest.headers.set("Cf-Access-Disk-Encrypted", firewallStatus)


       //sent modified request to origin
       return await fetch(newRequest)


       } catch (e) {
       console.log(e)
       return await fetch(request)
       }
       }
       return await fetch(request)
     },
   };
   ```

## 2. View the user's identity

The script in `index.js` uses the [`get-identity`](https://developers.cloudflare.com/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`.

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**.

```json
{
  "id": "P51Tuu01fWHMBjIBvrCK1lK-eUDWs2aQMv03WDqT5oY",
  "name": "John Doe",
  "email": "john.doe@cloudflare.com",
  "amr": [
    "pwd"
  ],
  "oidc_fields": {
    "principalName": "XXXXXX_cloudflare.com#EXT#@XXXXXXcloudflare.onmicrosoft.com"
  },
  "groups": [
    {
      "id": "fdaedb59-e9be-4ab7-8001-3e069da54185",
      "name": "XXXXX"
    }
  ],
  "idp": {
    "id": "b9f4d68e-dac1-48b0-b728-ae05a5f0d4b2",
    "type": "azureAD"
  },
  "geo": {
    "country": "FR"
  },
  "user_uuid": "ce40d564-c72f-475f-a9b8-f395f19ad986",
  "account_id": "121287a0c6e6260ec930655e6b39a3a8",
  "iat": 1724056537,
  "devicePosture": {
    "f6f9391e-6776-4878-9c60-0cc807dc7dc8": {
      "id": "f6f9391e-6776-4878-9c60-0cc807dc7dc8",
      "schedule": "5m",
      "timestamp": "2024-08-19T08:31:59.274Z",
      "description": "",
      "type": "disk_encryption",
      "check": {
        "drives": {
          "C": {
            "encrypted": true
          }
        }
      },
      "success": false,
      "rule_name": "Disk Encryption - Windows",
      "input": {
        "requireAll": true,
        "checkDisks": []
    },
    "a0a8e83d-be75-4aa6-bfa0-5791da6e9186": {
      "id": "a0a8e83d-be75-4aa6-bfa0-5791da6e9186",
      "schedule": "5m",
      "timestamp": "2024-08-19T08:31:59.274Z",
      "description": "",
      "type": "firewall",
      "check": {
        "firewall": false
      },
      "success": false,
      "rule_name": "Local Firewall Check - Windows",
      "input": {
        "enabled": true
      }
    }
    ...
  }
```

## 3. Route the Worker to your application

In the [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/), [set up a route](https://developers.cloudflare.com/workers/configuration/routing/routes/) that maps the Worker to your Access application domain:

* wrangler.jsonc

  ```jsonc
  {
    "route": {
      "pattern": "app.example.com/*",
      "zone_name": "example.com"
    }
  }
  ```

* wrangler.toml

  ```toml
  route = { pattern= "app.example.com/*", zone_name="example.com"}
  ```

## 4. Deploy the Worker

```sh
npx wrangler deploy
```

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.

```json
{
  "headers": {
    "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",
    "Accept-Encoding": "gzip",
    "Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-GB;q=0.6",
    "Cf-Access-Authenticated-User-Email": "John.Doe@cloudflare.com",
    "Cf-Access-Disk-Encrypted": "false",
    "Cf-Access-Firewall-Activated": "false",
    "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"
  }
}
```

You can verify that these headers are received by the origin server.
