solo enterprise agentgateway · entra jwt · anthropic
Dedicated gateway for Claude Desktop Interactive sign-in (PKCE) against Microsoft Entra ID. Strict JWT on the data plane, then Anthropic via the Vault-synced API key — no MDM required for lab use. On a laptop, use the loopback HTTP proxy so Claude Desktop stays on plain HTTP without fighting the lab’s self-signed TLS in Electron.
Claude Desktop authenticates with Entra (PKCE), sends an ID token as Bearer, the gateway validates it Strict against Entra JWKS, then injects the Anthropic key upstream.
PKCE → Entra ID token → loopback proxy → claude-desktop-gateway JWT Strict → anthropic-claude-desktop → api.anthropic.com
Path is / on this dedicated Gateway so Claude Desktop can call
{base}/v1/messages. The open Anthropic path for kagent stays on
anthropic-claude-gateway at :30398/anthropic.
Bare-metal NodePort on worker 172.16.10.155. Claude Desktop allows
plain HTTP only on loopback — so the recommended lab path is a local
TCP proxy to the gateway’s HTTP NodePort, not HTTPS into Electron.
On your laptop (lab LAN or VPN), run
python3
scripts/claude-desktop-lab-proxy.py
(or ~/bin/claude-desktop-lab-proxy.py if you already installed a copy).
Keep that process running. Set Claude Desktop’s Gateway base URL to
http://127.0.0.1:18789/.
The proxy forwards to 172.16.10.155:31938 (HTTP NodePort of
claude-desktop-gateway).
| Thing | Value |
|---|---|
| Service | claude-desktop-gateway · ns agentgateway-system |
| Claude Desktop base URL (lab) | http://127.0.0.1:18789/ · via scripts/claude-desktop-lab-proxy.py |
| Messages path (lab) | http://127.0.0.1:18789/v1/messages |
| Proxy upstream (HTTP NodePort) | 172.16.10.155:31938 · Service 80:31938/TCP · debug/curl OK without proxy |
| HTTPS NodePort (optional / advanced) | https://172.16.10.155:31211/ · 443:31211/TCP · TLS Terminate · Secret solo-ui-tls |
| Optional hostname | /etc/hosts → goose.local → https://goose.local:31211/ (cert SAN) — still needs OS trust |
Direct https://172.16.10.155:31211/ terminates with lab self-signed
solo-ui-tls. Claude Desktop / Electron commonly fails with
ERR_CERT_AUTHORITY_INVALID even when browsers can be taught to trust the cert.
Prefer the loopback proxy. If you insist on HTTPS, import solo-ui-tls
(CN/SAN IP 172.16.10.155, DNS goose.local) into the OS keychain
Claude Desktop uses — YMMV.
If ports ever change, re-confirm with
kubectl --context maniak-goose -n agentgateway-system get svc claude-desktop-gateway -o wide
(HTTP from port 80, HTTPS from port 443). Anonymous calls must 401/403; a valid Entra ID token reaches Anthropic.
Short SSO → chat slide deck from a working laptop session (loopback proxy base URL + Entra Interactive).
Source images:
assets/claude-desktop/.
Public client already registered for Claude Desktop’s loopback PKCE flow.
| Field | Value |
|---|---|
| App name | agw-claude-desktop |
| Tenant ID | 8635e970-2205-4189-bc77-77519ff5064f |
| Client ID | adf4a4f8-45a4-4bda-a7e2-35f39b1db59d |
| Issuer | https://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0 |
| Platform | Mobile and desktop applications |
| Redirect URI | http://127.0.0.1/callback |
| Public client flows | Yes (no client secret · PKCE) |
Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference…. Prefer the in-app UI for lab machines; fully quit and relaunch after edits. Full working lab values below (table + copyable JSON).
python3
scripts/claude-desktop-lab-proxy.py
→ leave it running → base URL http://127.0.0.1:18789/.
Deep-dive:
docs/claude-desktop-gateway.md.
| Field | Value |
|---|---|
| Connection / Inference provider | Gateway |
| Gateway base URL | http://127.0.0.1:18789/ (lab proxy; optional advanced: https://172.16.10.155:31211/) |
| Credential kind | Interactive sign-in |
| Client ID | adf4a4f8-45a4-4bda-a7e2-35f39b1db59d |
| Issuer URL | https://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0 |
| Bearer token | ID token (not Access token) |
| Scopes | openid email profile offline_access |
| Model discovery | On → http://127.0.0.1:18789/v1/models (see walkthrough slides 05–06) |
claude_desktop_config.json · lab values
Third-party inference runs in deploymentMode: 3p. Paste this
enterpriseConfig snippet (or merge into your local
claude_desktop_config.json) when not using MDM. Paths vary by OS
once in 3P mode.
{
"deploymentMode": "3p",
"enterpriseConfig": {
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "http://127.0.0.1:18789/",
"inferenceCredentialKind": "interactive",
"inferenceGatewayOidc": {
"clientId": "adf4a4f8-45a4-4bda-a7e2-35f39b1db59d",
"issuer": "https://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0",
"scopes": ["openid", "email", "profile", "offline_access"],
"bearerTokenType": "id_token"
}
}
}
inferenceGatewayBaseUrl must be the loopback proxy URL.
bearerTokenType: id_token matches gateway JWT
audiences = this app’s client ID.
offline_accessInclude offline_access so users are not re-prompted every ~1h when
using ID tokens. Fully quit and relaunch Claude Desktop after JSON edits.
Default Claude Desktop mode and this gateway’s aud = client ID.
Access tokens have a different audience story and will fail Strict JWT.
/callbackRegister http://127.0.0.1/callback under Mobile and desktop applications.
http://127.0.0.1 alone fails with AADSTS50011; the port is wildcarded.
Do not register the redirect under Web. Entra rejects the loopback PKCE callback on the Web platform. Public client, no secret.
GitOps under config/ via Argo app agentgateway-config. Push + sync is enough. No Gateway/JWT YAML changes for the laptop proxy path. File paths link to GitHub main.
| Kind | Name | File |
|---|---|---|
| Gateway | claude-desktop-gateway | config/gateway/claude-desktop-gateway.yaml |
| HTTPRoute | claude-desktop | config/routes/claude-desktop-route.yaml |
| AgentgatewayBackend | anthropic-claude-desktop | config/backends/anthropic-claude-desktop.yaml |
| AgentgatewayPolicy | claude-desktop-jwt-auth | config/policies/claude-desktop-jwt-auth.yaml |
| tracing targetRef | Gateway claude-desktop-gateway | config/policies/tracing.yaml |
| Lab proxy script | — | scripts/claude-desktop-lab-proxy.py |
| Walkthrough slides | — | assets/claude-desktop/ |
| Markdown deep-dive | — | docs/claude-desktop-gateway.md |
AgentgatewayBackend/entra-jwks ·
Secret/anthropic-secret (+ ExternalSecret → Vault llm-keys/anthropic) ·
Secret/solo-ui-tls (HTTPS terminate; same ns as Gateway) ·
EnterpriseAgentgatewayParameters/agentgateway-proxy-params (cost catalog)
Lab page, repo, proxy script, and GitOps manifests for the Claude Desktop path.
scripts/claude-desktop-lab-proxy.pydocs/claude-desktop-gateway.mdconfig/gateway/claude-desktop-gateway.yamlconfig/routes/claude-desktop-route.yamlconfig/backends/anthropic-claude-desktop.yamlconfig/policies/claude-desktop-jwt-auth.yamlassets/claude-desktop/