solo enterprise agentgateway · entra jwt · anthropic

Claude Desktop Gateway · 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.

Lab · http://127.0.0.1:18789/ ID token · Strict JWT /v1/messages reuses entra-jwks + anthropic-secret
Lab page Markdown deep-dive Proxy script k8s-goose repo All links ↓

#Architecture

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.

1 · PKCE
Entra Interactive
public client
2 · Bearer
ID token
not access token
3 · Gateway
JWT Strict
entra-jwks
4 · Upstream
Anthropic
anthropic-secret

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.

#Live endpoint

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.

Recommended lab path — loopback proxy

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

ThingValue
Serviceclaude-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/hostsgoose.localhttps://goose.local:31211/ (cert SAN) — still needs OS trust
HTTPS :31211 is optional — Electron often rejects the lab cert

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.

Confirm NodePorts

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.

#Lab walkthrough

Short SSO → chat slide deck from a working laptop session (loopback proxy base URL + Entra Interactive). Source images: assets/claude-desktop/.

Claude Desktop welcome: Sign in with your organization via custom inference gateway
01 · Welcome

Sign in with your organization — custom inference gateway; no Claude.ai account needed.

Microsoft Entra consent for agw-claude-desktop as sebastian@maniak.io
02 · Entra consent

agw-claude-desktop requests profile + offline access for sebastian@maniak.io.

Signed-in Claude Desktop home: You're here, Claude Opus 5 High, Maniak Gateway profile
03 · Signed in (primary)

You’re here! · model Claude Opus 5 High · profile sebastian@maniak.io / Maniak Gateway.

Claude Desktop Configure third-party inference Connection pane with Interactive sign-in and loopback model discovery
05 · Connection form

Lab Connection pane once Gateway base URL is http://127.0.0.1:18789/ and Credential kind is Interactive sign-in — Client ID, full Entra issuer, Bearer ID token, scopes openid email profile offline_access, Model discovery ON → /v1/models.

Close-up of Model discovery ON for 127.0.0.1:18789/v1/models and Test model discovery
06 · Model discovery

Model discovery ON against 127.0.0.1:18789 · Test model discovery — auto-fills the picker from the lab proxy.

Working chat: whats 2+2 answers 4; footer sebastian Gateway
04 · Chat works

whats 2+24 · footer sebastian · Gateway — inference through the lab gateway.

#Entra app

Public client already registered for Claude Desktop’s loopback PKCE flow.

FieldValue
App nameagw-claude-desktop
Tenant ID8635e970-2205-4189-bc77-77519ff5064f
Client IDadf4a4f8-45a4-4bda-a7e2-35f39b1db59d
Issuerhttps://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0
PlatformMobile and desktop applications
Redirect URIhttp://127.0.0.1/callback
Public client flowsYes (no client secret · PKCE)

#Claude Desktop developer config

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

Start the proxy first

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.

FieldValue
Connection / Inference providerGateway
Gateway base URLhttp://127.0.0.1:18789/ (lab proxy; optional advanced: https://172.16.10.155:31211/)
Credential kindInteractive sign-in
Client IDadf4a4f8-45a4-4bda-a7e2-35f39b1db59d
Issuer URLhttps://login.microsoftonline.com/8635e970-2205-4189-bc77-77519ff5064f/v2.0
Bearer tokenID token (not Access token)
Scopesopenid email profile offline_access
Model discoveryOnhttp://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.

claude_desktop_config.json · enterpriseConfig (lab)
{
  "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"
    }
  }
}
interactive · id_token

Match the table

inferenceGatewayBaseUrl must be the loopback proxy URL. bearerTokenType: id_token matches gateway JWT audiences = this app’s client ID.

refresh

offline_access

Include offline_access so users are not re-prompted every ~1h when using ID tokens. Fully quit and relaunch Claude Desktop after JSON edits.

#Critical callouts

Bearer must be an ID token

Default Claude Desktop mode and this gateway’s aud = client ID. Access tokens have a different audience story and will fail Strict JWT.

Redirect must include /callback

Register http://127.0.0.1/callback under Mobile and desktop applications. http://127.0.0.1 alone fails with AADSTS50011; the port is wildcarded.

Platform = Mobile and desktop — not Web

Do not register the redirect under Web. Entra rejects the loopback PKCE callback on the Web platform. Public client, no secret.

#Cluster resources

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.

KindNameFile
Gatewayclaude-desktop-gatewayconfig/gateway/claude-desktop-gateway.yaml
HTTPRouteclaude-desktopconfig/routes/claude-desktop-route.yaml
AgentgatewayBackendanthropic-claude-desktopconfig/backends/anthropic-claude-desktop.yaml
AgentgatewayPolicyclaude-desktop-jwt-authconfig/policies/claude-desktop-jwt-auth.yaml
tracing targetRefGateway claude-desktop-gatewayconfig/policies/tracing.yaml
Lab proxy scriptscripts/claude-desktop-lab-proxy.py
Walkthrough slidesassets/claude-desktop/
Markdown deep-divedocs/claude-desktop-gateway.md
Reused — do not recreate

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)