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.
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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
