Skip to content

Conversation

@mads-digitial-solutions
Copy link

Static Access Token Mode

Overview

Add support for providing a Google access token via environment variable, bypassing the OAuth flow entirely. This enables use cases where an external system handles OAuth and provides tokens to this server.

Use Case

Service account replacement - an external system performs OAuth authentication and provides a valid Google access token. This server uses that token for all Google API requests without managing the OAuth flow itself.

Behavior

  • Token refresh: External system responsibility. If the token expires, API calls fail with 401.
  • User identity: Fetched from Google's userinfo endpoint at startup to validate the token.
  • Mode exclusivity: Mutually exclusive with OAuth 2.1 and external OAuth provider modes.
  • Stateless behavior: Operates like stateless mode - no credential file operations or directory checks.

Configuration

New environment variable:

Variable Description
GOOGLE_ACCESS_TOKEN Google access token. When set, enables static token mode.

When GOOGLE_ACCESS_TOKEN is set:

  • OAuth flow is disabled
  • All API requests use the provided token
  • User email is determined automatically via Google userinfo API
  • Credentials directory check is skipped (nothing to persist)
  • OAuth callback server is not started (no OAuth flow needed)
  • Startup config display shows only relevant variables

Implementation

1. OAuth Config (/auth/oauth_config.py)

Add properties to OAuthConfig:

@property
def static_access_token(self) -> str | None:
    """Google access token provided via environment (bypasses OAuth)."""
    return os.environ.get("GOOGLE_ACCESS_TOKEN")

@property
def is_static_token_mode(self) -> bool:
    """Whether static token mode is enabled."""
    return self.static_access_token is not None

2. Google Auth (/auth/google_auth.py)

Add module-level state:

_static_token_user_email: str | None = None

Add validation function:

async def validate_static_token() -> str:
    """
    Validate the static access token and return the user's email.
    Called once at startup when GOOGLE_ACCESS_TOKEN is set.
    Raises GoogleAuthenticationError if token is invalid.
    """
    # Call https://www.googleapis.com/oauth2/v2/userinfo
    # Return user email on success
    # Raise with clear message on failure

Add credential builder:

def _build_static_credentials() -> Credentials:
    """Build a Credentials object from the static access token."""
    return Credentials(token=oauth_config.static_access_token)

Update get_credentials() - add check at top:

if oauth_config.is_static_token_mode:
    return _build_static_credentials()

Update start_auth_flow() - add check at top:

if oauth_config.is_static_token_mode:
    raise GoogleAuthenticationError(
        "OAuth flow not available in static token mode. "
        "Provide a valid GOOGLE_ACCESS_TOKEN environment variable."
    )

3. Main (/main.py)

Startup validation - validate token before server starts:

oauth_config = get_oauth_config()
if oauth_config.is_static_token_mode():
    import asyncio
    from auth.google_auth import validate_static_token
    user_email = asyncio.run(validate_static_token())
    # Server proceeds if valid, exits if invalid

Skip credentials directory check - not needed in static token mode:

if not is_stateless_mode() and not oauth_config.is_static_token_mode():
    check_credentials_directory_permissions()
else:
    # Log why we're skipping (stateless or static token mode)

Skip OAuth callback server - no OAuth flow in static token mode:

if args.transport == "stdio":
    if not oauth_config.is_static_token_mode():
        # Start OAuth callback server
        ensure_oauth_callback_available(...)
    else:
        # Skip - not needed for static token mode

Simplified config display - only show relevant variables:

if oauth_config.is_static_token_mode():
    config_vars = {
        "GOOGLE_ACCESS_TOKEN": "***SET***",
    }
else:
    config_vars = {
        "GOOGLE_OAUTH_CLIENT_ID": ...,
        "GOOGLE_OAUTH_CLIENT_SECRET": ...,
        # ... other OAuth config vars
    }

Files Changed

File Changes
/auth/oauth_config.py Add static_access_token and is_static_token_mode properties, mutual exclusivity validation
/auth/google_auth.py Add validate_static_token(), _build_static_credentials(), module-level _static_token_user_email, update get_credentials() and start_auth_flow()
/main.py Add startup validation, skip credentials directory check, skip OAuth callback server, conditional config display

Error Scenarios

Scenario Behavior
Invalid token at startup Server fails to start with clear error message
Token expires mid-session API calls return 401; error indicates external refresh needed
OAuth flow attempted Returns error explaining OAuth not available in this mode
Conflicting modes enabled Startup error for invalid configuration (GOOGLE_ACCESS_TOKEN + MCP_ENABLE_OAUTH21)

@mads-digitial-solutions
Copy link
Author

Removed Static Token Validation

Why

In static token mode, the server was calling Google's userinfo endpoint at startup to validate the token and fetch the user's email. This required the userinfo.email scope on the token.

Since the token is provided externally, the caller already knows the user identity. The userinfo call was unnecessary and added a scope requirement that shouldn't be needed.

What Changed

  • Removed validate_static_token() function
  • Removed get_static_token_user_email() function
  • Removed startup validation call in main.py

The server now trusts the provided token and uses it directly for API requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant