Skip to content

Google OAuth Provider

Implements OAuthAuthorizationServerProvider from the MCP SDK, delegating identity verification to Google while owning the full OAuth 2.1 state machine.

Overview

The provider handles:

  • Dynamic Client Registration — MCP clients register themselves on first connection.
  • Authorization — Redirects clients to Google's authorization endpoint; stores pending auth state in GCS.
  • Google callback — Exchanges the Google code for a userinfo token, verifies the email, and mints an MCP authorization code.
  • Token exchange — Exchanges MCP authorization codes for access/refresh token pairs.
  • Token refresh — Issues new access tokens from valid refresh tokens.
  • Token revocation — Removes both the access token and all associated refresh tokens (or vice versa).

State persistence

All OAuth state is stored in a single JSON blob at oauth-state/state.json in the GCS bucket configured by OAUTH_STATE_BUCKET. The blob is read at startup and written synchronously after every mutation. This ensures sessions survive Cloud Run container restarts and scale-to-zero events.

The blob contains:

{
  "clients": { "<client_id>": { ... } },
  "auth_codes": { "<code>": { ... } },
  "access_tokens": { "<token>": { ... } },
  "refresh_tokens": { "<token>": { ... } },
  "pending_auths": { "<google_state>": { ... } }
}

Expired tokens are filtered out on load and never written back.

Token lifetimes

Token TTL
Access token 3,600 s (1 hour)
Refresh token 2,592,000 s (30 days)
Authorization code 300 s (5 minutes)
Pending auth state 600 s (10 minutes)

Module reference

the_curator.auth.provider

Google OAuth 2.0 delegation provider for the MCP authorization server.

Implements OAuthAuthorizationServerProvider so the MCP SDK handles all protocol-level OAuth 2.1 machinery. Identity verification is delegated to Google; only the configured allowed_email can authenticate.

State is persisted to a GCS blob so tokens survive Cloud Run restarts.

logger = logging.getLogger(__name__) module-attribute

GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth' module-attribute

GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token' module-attribute

GOOGLE_USERINFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo' module-attribute

ACCESS_TOKEN_TTL = 3600 module-attribute

REFRESH_TOKEN_TTL = 86400 * 30 module-attribute

AUTH_CODE_TTL = 300 module-attribute

STATE_BLOB_NAME = 'oauth-state/state.json' module-attribute

GoogleOAuthProvider

OAuth provider that delegates identity verification to Google.

Single-user: only allowed_email can authenticate. All OAuth state is kept in memory and persisted to GCS after every mutation so container restarts don't invalidate active sessions.

__init__(google_client_id: str, google_client_secret: str, allowed_email: str, server_url: str, gcs_bucket: str) -> None

get_client(client_id: str) -> OAuthClientInformationFull | None async

register_client(client_info: OAuthClientInformationFull) -> None async

authorize(client: OAuthClientInformationFull, params: AuthorizationParams) -> str async

handle_google_callback(code: str, state: str) -> str async

Exchange Google code for identity, verify email, mint MCP auth code.

Returns the redirect URL to send the browser back to the MCP client. Raises ValueError if state is invalid or the user is not authorized.

load_authorization_code(client: OAuthClientInformationFull, authorization_code: str) -> AuthorizationCode | None async

exchange_authorization_code(client: OAuthClientInformationFull, authorization_code: AuthorizationCode) -> OAuthToken async

load_refresh_token(client: OAuthClientInformationFull, refresh_token: str) -> RefreshToken | None async

exchange_refresh_token(client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]) -> OAuthToken async

load_access_token(token: str) -> AccessToken | None async

revoke_token(token: AccessToken | RefreshToken) -> None async