diff --git a/mcp_bridge/mcp_server.py b/mcp_bridge/mcp_server.py index 5925f35..391579f 100644 --- a/mcp_bridge/mcp_server.py +++ b/mcp_bridge/mcp_server.py @@ -5,27 +5,66 @@ import logging from datetime import datetime, timezone from fastmcp import FastMCP -from fastmcp.server.auth.providers.in_memory import InMemoryOAuthProvider -from fastmcp.server.auth.auth import ClientRegistrationOptions +import contextlib +import httpx +from fastmcp.server.auth import TokenVerifier, AccessToken +from fastmcp.server.auth.oauth_proxy import OAuthProxy from starlette.requests import Request from starlette.responses import JSONResponse from starlette.routing import Route from .db import Database -from .config import get_group_chat_id +from .config import get_group_chat_id, load_credentials logger = logging.getLogger(__name__) db: Database | None = None -oauth = InMemoryOAuthProvider( +FORGEJO_URL = "https://git.georgsen.dk" + + +class ForgejoTokenVerifier(TokenVerifier): + """Verify OAuth tokens against Forgejo's API.""" + + def __init__(self, forgejo_url: str = FORGEJO_URL): + super().__init__(required_scopes=None) + self.forgejo_url = forgejo_url + + async def verify_token(self, token: str) -> AccessToken | None: + try: + async with httpx.AsyncClient(timeout=10) as client: + resp = await client.get( + f"{self.forgejo_url}/api/v1/user", + headers={"Authorization": f"Bearer {token}"}, + ) + if resp.status_code != 200: + return None + user = resp.json() + return AccessToken( + token=token, + client_id=str(user["id"]), + scopes=[], + expires_at=None, + claims={"sub": str(user["id"]), "login": user.get("login")}, + ) + except Exception as e: + logger.debug(f"Forgejo token verification failed: {e}") + return None + + +creds = load_credentials() +auth = OAuthProxy( + upstream_authorization_endpoint=f"{FORGEJO_URL}/login/oauth/authorize", + upstream_token_endpoint=f"{FORGEJO_URL}/login/oauth/access_token", + upstream_client_id=creds["FORGEJO_OAUTH_CLIENT_ID"], + upstream_client_secret=creds["FORGEJO_OAUTH_CLIENT_SECRET"], + token_verifier=ForgejoTokenVerifier(), base_url="https://mcp.georgsen.dk", - client_registration_options=ClientRegistrationOptions(enabled=True), ) mcp = FastMCP( name="homelab-bridge", - auth=oauth, + auth=auth, instructions=( "This MCP server bridges claude.ai to a homelab Telegram group chat. " "Use pull_updates to read conversation history (supports cursor-based pagination). "