Document Entra oauth2-proxy deployment

Co-authored-by: Luke The Dev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-04-27 03:35:05 +00:00
co-authored by Luke The Dev
parent 0de07bfa98
commit 9d4a0898bf
3 changed files with 298 additions and 0 deletions
+4
View File
@@ -224,6 +224,9 @@ Alternative with SSH:
3. Set `STUDIO_ACCESS_TOKEN` if Studio binds to a public host.
4. Configure the gateway URL and token inside Studio.
For Entra ID / Azure AD deployments with nginx, Let's Encrypt, and oauth2-proxy,
see [`docs/deployment/entra-oauth2-proxy.md`](docs/deployment/entra-oauth2-proxy.md).
### Studio on LAN or Tailscale for other devices
1. Start Studio with `HOST=0.0.0.0` (or a specific LAN/Tailscale host).
@@ -296,6 +299,7 @@ See [`.env.example`](.env.example) for the full local development template.
- [`docs/pi-chat-streaming.md`](docs/pi-chat-streaming.md): gateway runtime streaming and transcript rendering.
- [`docs/permissions-sandboxing.md`](docs/permissions-sandboxing.md): Studio permissions and OpenClaw behavior.
- [`docs/hermes-gateway.md`](docs/hermes-gateway.md): Hermes adapter setup, capabilities, and current limitations.
- [`docs/deployment/entra-oauth2-proxy.md`](docs/deployment/entra-oauth2-proxy.md): Entra ID, oauth2-proxy, nginx, and TLS deployment recipe.
## Current Limitations
+1
View File
@@ -43,3 +43,4 @@ Please report issues related to:
- In production, set `CUSTOM_RUNTIME_ALLOWLIST` if you use `/api/runtime/custom`. If unset, it falls back to `UPSTREAM_ALLOWLIST`.
- Empty allowlists are intended for local development only.
- If you enable `STUDIO_ACCESS_TOKEN`, you must also provision the `studio_access` cookie through your deployment/auth layer.
- For Entra ID / oauth2-proxy deployments, see [`docs/deployment/entra-oauth2-proxy.md`](docs/deployment/entra-oauth2-proxy.md).
+293
View File
@@ -0,0 +1,293 @@
# Deploy Claw3D Behind Entra ID And oauth2-proxy
This guide describes a production deployment pattern for Claw3D on an Azure VM
behind nginx, Let's Encrypt, oauth2-proxy, and Microsoft Entra ID.
Use this when you want enterprise SSO in front of Claw3D without adding a
built-in OIDC login flow to the app itself.
## Topology
```text
Browser
-> https://claw3d.example.com
-> nginx / Let's Encrypt
-> oauth2-proxy / Entra ID
-> Claw3D Studio on 127.0.0.1:3000
-> OpenClaw Gateway over ws:// or wss:// from the Studio host
```
Claw3D still owns the Studio UI and same-origin gateway WebSocket proxy. Entra
ID and oauth2-proxy own user authentication before requests reach Claw3D.
## Prerequisites
- Azure VM with Node.js 20+ and npm 10+.
- DNS record for the Claw3D host, for example `claw3d.example.com`.
- nginx installed on the VM.
- TLS certificate from Let's Encrypt or another trusted CA.
- oauth2-proxy installed on the VM.
- An Entra ID tenant where you can create an app registration.
- A running OpenClaw Gateway reachable from the Claw3D Studio host.
## Entra ID app registration
Create an Entra ID app registration for oauth2-proxy.
Recommended settings:
- Platform type: Web.
- Redirect URI:
```text
https://claw3d.example.com/oauth2/callback
```
- Supported account type: choose the tenant policy your organization requires.
- Client secret: create one and store it only on the VM.
- API permissions:
- `openid`
- `profile`
- `email`
Record these values:
- Tenant ID.
- Client ID.
- Client secret.
## Claw3D environment
Run Claw3D on loopback and let nginx expose it publicly.
Example `.env` values:
```bash
HOST=127.0.0.1
PORT=3000
TRUSTED_PROXY=1
UPSTREAM_ALLOWLIST=openclaw-gateway.example.com,localhost,127.0.0.1
CLAW3D_GATEWAY_URL=wss://openclaw-gateway.example.com
CLAW3D_GATEWAY_ADAPTER_TYPE=openclaw
CLAW3D_OFFICE_EVENTS_SECRET=<long-random-webhook-secret>
```
Notes:
- Keep `HOST=127.0.0.1` when nginx and oauth2-proxy are on the same VM.
- Set `TRUSTED_PROXY=1` only when nginx is the only public entry point and
direct public access to Claw3D is blocked.
- Set `UPSTREAM_ALLOWLIST` in production so Studio only proxies approved gateway
hosts.
- `CLAW3D_OFFICE_EVENTS_SECRET` is only needed if you use
`POST /api/office/events`.
`STUDIO_ACCESS_TOKEN` is optional in this deployment. oauth2-proxy is the main
browser-facing auth gate. If you also enable `STUDIO_ACCESS_TOKEN`, configure
oauth2-proxy or nginx to issue the expected `studio_access` cookie, otherwise
Claw3D will return `401 Studio access token required`.
Start Claw3D:
```bash
npm run build
npm run start
```
Use systemd, pm2, or another process manager for production.
## oauth2-proxy configuration
Example `/etc/oauth2-proxy/claw3d.cfg`:
```ini
provider = "oidc"
oidc_issuer_url = "https://login.microsoftonline.com/<tenant-id>/v2.0"
client_id = "<application-client-id>"
client_secret = "<application-client-secret>"
redirect_url = "https://claw3d.example.com/oauth2/callback"
email_domains = [ "*" ]
scope = "openid profile email"
cookie_secret = "<base64-32-byte-secret>"
cookie_secure = true
cookie_httponly = true
cookie_samesite = "lax"
cookie_expire = "8h"
cookie_refresh = "1h"
reverse_proxy = true
set_xauthrequest = true
pass_authorization_header = true
pass_access_token = false
upstreams = [ "http://127.0.0.1:3000" ]
http_address = "127.0.0.1:4180"
```
Generate a cookie secret:
```bash
python3 - <<'PY'
import base64, os
print(base64.urlsafe_b64encode(os.urandom(32)).decode())
PY
```
Restrict access further with oauth2-proxy options such as `allowed_groups` or
`allowed_email_domains` if your Entra tenant requires it.
## nginx configuration
Example server block:
```nginx
server {
listen 80;
server_name claw3d.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name claw3d.example.com;
ssl_certificate /etc/letsencrypt/live/claw3d.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/claw3d.example.com/privkey.pem;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location = /oauth2/auth {
internal;
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/start;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/gateway/ws {
auth_request /oauth2/auth;
error_page 401 = /oauth2/start;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
```
The explicit `/api/gateway/ws` location is important. Claw3D uses this
same-origin WebSocket endpoint for browser-to-Studio gateway traffic.
## Webhook ingress
If you expose `POST /api/office/events` to systems such as HubSpot or CI, keep
the endpoint behind the same TLS host and require signed payloads.
Example signed request:
```bash
body='{"source":"hubspot","eventType":"deal.closed","title":"Deal closed","message":"Automotive account signed.","effect":"confetti"}'
ts="$(date +%s000)"
sig="sha256=$(printf '%s.%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$CLAW3D_OFFICE_EVENTS_SECRET" -hex | awk '{print $2}')"
curl -X POST https://claw3d.example.com/api/office/events \
-H "Content-Type: application/json" \
-H "X-Claw3D-Timestamp: $ts" \
-H "X-Claw3D-Signature: $sig" \
--data "$body"
```
Recommended nginx hardening for webhooks:
- Keep TLS enabled.
- Keep request body size small.
- Allowlist source IP ranges when your provider publishes stable ranges.
- Rotate `CLAW3D_OFFICE_EVENTS_SECRET` if it is exposed.
## Health checks
From the VM:
```bash
curl -I http://127.0.0.1:3000/
curl -I http://127.0.0.1:4180/oauth2/start
```
From a browser:
1. Open `https://claw3d.example.com/office`.
2. Confirm Entra redirects you to sign in.
3. Confirm the office UI loads after sign-in.
4. Confirm the browser WebSocket connects through `/api/gateway/ws`.
5. Confirm the configured OpenClaw Gateway is reachable from the Studio host.
## Troubleshooting
### Redirect loop
- Confirm `redirect_url` in oauth2-proxy exactly matches the Entra redirect URI.
- Confirm nginx sends `X-Forwarded-Proto: https`.
- Confirm oauth2-proxy has `reverse_proxy = true`.
### WebSocket does not connect
- Confirm nginx has a dedicated `/api/gateway/ws` location.
- Confirm `proxy_set_header Upgrade $http_upgrade`.
- Confirm `proxy_set_header Connection "upgrade"`.
- Confirm oauth2-proxy protects the WebSocket route with `auth_request`.
### `401 Studio access token required`
This means `STUDIO_ACCESS_TOKEN` is enabled but the expected `studio_access`
cookie is not present. Either disable `STUDIO_ACCESS_TOKEN` and rely on
oauth2-proxy, or configure your deployment to issue the cookie before requests
reach Claw3D.
### Gateway proxy blocked
Set `UPSTREAM_ALLOWLIST` to include the upstream OpenClaw Gateway host. For
direct runtime integrations, also set `CUSTOM_RUNTIME_ALLOWLIST` or include the
runtime host in `UPSTREAM_ALLOWLIST`.
### User can reach Claw3D without Entra
- Confirm Claw3D listens only on `127.0.0.1`.
- Confirm the VM firewall does not expose port `3000`.
- Confirm nginx is the only public listener.
- Confirm `TRUSTED_PROXY=1` is only set behind your controlled nginx proxy.