From eb351b5e85cb3371baf35b8de1f594ecd2a70abe Mon Sep 17 00:00:00 2001 From: Chris Christiansen Date: Wed, 17 Jun 2026 16:57:54 +0000 Subject: [PATCH] fix(opax-mcp): add IAP identity token auth for opax.vauco.no calls Add _iap_token() that fetches identity token from GCP metadata server using IAP client ID as audience. Update _opax_get and _opax_post to send Authorization: Bearer header on all calls to opax.vauco.no. Fixes 401 Unauthorized from IAP-protected opax.vauco.no backend. Related: LEARNING-013 --- opax-mcp/server.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/opax-mcp/server.py b/opax-mcp/server.py index a96a587..722ea6c 100644 --- a/opax-mcp/server.py +++ b/opax-mcp/server.py @@ -12,6 +12,16 @@ from typing import Optional, Any app = FastAPI(title="opax-mcp", version="2.0.0") OPAX_BASE_URL = os.environ.get("OPAX_BASE_URL", "https://opax.vauco.no") +IAP_CLIENT_ID = os.environ.get("IAP_CLIENT_ID", "357036551735-ka7t2fv9ue2jp01bs826hpdctlvispuo.apps.googleusercontent.com") + +async def _iap_token() -> str: + """Fetch IAP identity token from GCP metadata server.""" + url = (f"http://metadata.google.internal/computeMetadata/v1/instance/" + f"service-accounts/default/identity?audience={IAP_CLIENT_ID}&format=full") + async with httpx.AsyncClient(timeout=10) as c: + r = await c.get(url, headers={"Metadata-Flavor": "Google"}) + r.raise_for_status() + return r.text.strip() MCP_SECRET = os.environ.get("MCP_SECRET", "") # Gitea @@ -69,15 +79,19 @@ async def list_tools(x_mcp_secret: Optional[str] = Header(default=None)): # --------------------------------------------------------------------------- async def _opax_get(path: str) -> Any: + token = await _iap_token() async with httpx.AsyncClient(timeout=30) as client: - r = await client.get(f"{OPAX_BASE_URL}{path}") + r = await client.get(f"{OPAX_BASE_URL}{path}", + headers={"Authorization": f"Bearer {token}"}) r.raise_for_status() return r.json() async def _opax_post(path: str, body: dict) -> Any: + token = await _iap_token() async with httpx.AsyncClient(timeout=30) as client: - r = await client.post(f"{OPAX_BASE_URL}{path}", json=body) + r = await client.post(f"{OPAX_BASE_URL}{path}", json=body, + headers={"Authorization": f"Bearer {token}"}) r.raise_for_status() return r.json()