Secure Your MCP‑Based LLM Apps with Google OAuth 2.0: A Step‑by‑Step Python Demo

This guide walks you through the fundamentals of OAuth 2.0, explains how the MCP protocol supports it, and provides a complete Python example that integrates Google OAuth 2.0 into an MCP server and client, covering configuration, code implementation, and testing procedures.

AI Large Model Application Practice
AI Large Model Application Practice
AI Large Model Application Practice
Secure Your MCP‑Based LLM Apps with Google OAuth 2.0: A Step‑by‑Step Python Demo

OAuth 2.0 Core Concepts

OAuth 2.0 is an authorization framework that enables third‑party applications to obtain limited access to protected resources without exposing user passwords. The main roles are:

Resource Owner : the user who owns the protected data.

Client : the third‑party app that requests access on behalf of the user.

Authorization Server : authenticates the user and issues authorization codes and tokens.

Resource Server : stores the protected resources and validates access tokens.

MCP Support for OAuth 2.0

The MCP protocol adds a layer that forwards client requests to an external OAuth provider (e.g., Google). Compared with a pure OAuth flow, MCP initiates the redirect to the third‑party provider and receives the authorization code on behalf of the client, then issues its own MCP token bound to the third‑party token.

Google OAuth 2.0 Integration Example (Python + MCP SDK)

Environment preparation

Install the latest MCP Python SDK.

Register a Google OAuth application (Web type) and obtain a client ID, client secret, and redirect URI (e.g., http://localhost:3000/callback).

Server configuration (Pydantic settings)

from pydantic import AnyHttpUrl
from pydantic_settings import BaseSettings, SettingsConfigDict

class ServerSettings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="MCP_")
    # MCP Server
    host: str = "localhost"
    port: int = 3000
    server_url: AnyHttpUrl = AnyHttpUrl("http://localhost:3000")
    callback_path: str = "http://localhost:3000/callback"
    # Google OAuth
    client_id: str  # set via MCP_CLIENT_ID env var
    client_secret: str  # set via MCP_CLIENT_SECRET env var
    auth_url: str = "https://accounts.google.com/o/oauth2/auth"
    token_url: str = "https://oauth2.googleapis.com/token"
    scope: str = (
        "https://www.googleapis.com/auth/userinfo.email "
        "https://www.googleapis.com/auth/userinfo.profile openid"
    )

MCP Server side – OAuth provider implementation

class GoogleOAuthProvider(OAuthAuthorizationServerProvider):
    def __init__(self, settings: ServerSettings):
        self.settings = settings
        self.clients: Dict[str, OAuthClientInformationFull] = {}
        self.auth_codes: Dict[str, AuthorizationCode] = {}
        self.tokens: Dict[str, Dict] = {}
        self.state_mapping: Dict[str, Dict[str, str]] = {}

    async def authorize(self, client, params) -> str:
        state = secrets.token_hex(16)
        self.state_mapping[state] = {
            "redirect_uri": str(params.redirect_uri),
            "client_id": client.client_id,
            "code_challenge": getattr(params, "code_challenge", None),
        }
        auth_params = {
            "client_id": self.settings.client_id,
            "redirect_uri": self.settings.callback_path,
            "response_type": "code",
            "scope": self.settings.scope,
            "state": state,
            "access_type": "online",
        }
        auth_url = f"{self.settings.auth_url}?{urllib.parse.urlencode(auth_params)}"
        return auth_url

    async def handle_callback(self, code: str, state: str) -> str:
        state_data = self.state_mapping.get(state)
        if not state_data:
            raise HTTPException(400, "Invalid state parameter")
        token_response = await create_mcp_http_client().post(
            self.settings.token_url,
            data=urllib.parse.urlencode({
                "client_id": self.settings.client_id,
                "client_secret": self.settings.client_secret,
                "code": code,
                "redirect_uri": self.settings.callback_path,
                "grant_type": "authorization_code",
            }),
            headers={"Content-Type": "application/x-www-form-urlencoded"},
        )
        token_response.raise_for_status()
        google_data = token_response.json()
        mcp_code = f"mcp_{secrets.token_hex(16)}"
        self.auth_codes[mcp_code] = AuthorizationCode(
            code=mcp_code,
            client_id=state_data["client_id"],
            redirect_uri=state_data["redirect_uri"],
            redirect_uri_provided_explicitly=True,
            expires_at=time.time() + 300,
            scopes=self.settings.scope.split(),
            code_challenge=state_data.get("code_challenge", ""),
        )
        self.tokens[mcp_code] = google_data
        del self.state_mapping[state]
        return construct_redirect_uri(state_data["redirect_uri"], code=mcp_code, state=state)

    async def load_authorization_code(self, client, authorization_code):
        return self.auth_codes.get(authorization_code)

    async def exchange_authorization_code(self, client, authorization_code):
        token_data = self.tokens.get(authorization_code)
        if token_data:
            return token_data.get("access_token")
        return None

    async def load_access_token(self, token):
        token_data = self.tokens.get(token)
        if not token_data:
            return None
        if token_data.get("expires_at", 0) < time.time():
            del self.tokens[token]
            return None
        return AccessToken(
            token=token,
            client_id=token_data["client_id"],
            scopes=self.settings.scope.split(),
            expires_at=int(token_data.get("expires_at", 0)),
        )

Starting the MCP server (FastMCP)

oauth_provider = GoogleOAuthProvider(settings)
auth_settings = AuthSettings(
    issuer_url=settings.server_url,
    client_registration_options=ClientRegistrationOptions(enabled=True),
    required_scopes=["openid"],
)
app = FastMCP(
    name="Google OAuth MCP Server",
    auth_server_provider=oauth_provider,
    host=settings.host,
    port=settings.port,
    auth=auth_settings,
)

@app.custom_route("/callback", methods=["GET"])
async def callback_handler(request: Request) -> Response:
    code = request.query_params.get("code")
    state = request.query_params.get("state")
    redirect_uri = await oauth_provider.handle_callback(code, state)
    return RedirectResponse(url=redirect_uri)

app.run()

MCP client side

The client uses OAuthClientProvider from the MCP SDK. The provider launches a local HTTP server, opens the user's browser to the Google consent page, receives the MCP authorization code, exchanges it for an MCP access token, and then can call protected MCP resources.

Testing with MCP Inspector

Run the inspector tool with npx @modelcontextprotocol/inspector, configure the transport and server URL, and step through the OAuth flow using the built‑in OAuth helper to observe each request and response.

Alternative authentication strategies

Simple API‑Key authentication.

OAuth Client Credentials flow (no user interaction).

Hybrid approaches that combine token‑based and key‑based methods.

Source code

Full example repository: https://github.com/pingcy/mcp-oauth-demo

OAuth flow diagram
OAuth flow diagram
Authorization code flow diagram
Authorization code flow diagram
MCP server OAuth integration diagram
MCP server OAuth integration diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonLLMMCPAuthenticationGoogleOAuth2
AI Large Model Application Practice
Written by

AI Large Model Application Practice

Focused on deep research and development of large-model applications. Authors of "RAG Application Development and Optimization Based on Large Models" and "MCP Principles Unveiled and Development Guide". Primarily B2B, with B2C as a supplement.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.