Designing MCP Authentication Architecture for Zero-Touch OAuth Implementation

Recently, I encountered an interesting technological trend called ‘Zero-Touch OAuth for MCP’. During the integration of MCP (Model Context Protocol) with our ZeroClaw runtime and blog-api-server that we are developing, ensuring security without compromising user experience is an essential task. In this post, we will design a ‘Zero-Touch’ authentication flow that helps users connect to MCP clients without any complex separate configurations, and explore how to implement it in actual code.

Why Zero-Touch?

Traditional OAuth 2.0 flows often require users to obtain a ‘Client ID’ and ‘Client Secret’ and enter them into a configuration file. However, for general users or developers, this presents a significant barrier to entry. Zero-Touch OAuth aims for a flow where the client automatically attempts authentication through pre-registered metadata, and users simply need to click an ‘Allow’ button.

Architecture Design

We will configure blog-api-server as the MCP Provider and Claude Code or custom clients as MCP Consumers. The core idea is to build a Public Client environment that does not store Secrets by using the PKCE (Proof Key for Code Exchange) extension.

Core Components:

  1. MCP Client (Consumer): An agent running in the user’s local environment.
  2. Auth Server: The OAuth2 authentication server implemented within blog-api-server.
  3. Resource Server: The actual blog API.

Implementation Step 1: Server Side (blog-api-server)

First, we need to add a simple authentication endpoint to the Rust-based blog-api-server. We will extend the architecture mentioned in the previous post, ’[blog-api-server] Adding Language Parameter to MCP Blog Client’, to make the Auth module independent.

Here, we will show the core logic: the Verification logic. This is the process of verifying the code_verifier received from the client.

// blog-api-server/src/auth/handler.rs
use axum::{extract::State, Json};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use base64::Engine as _;

#[derive(Deserialize)]
pub struct TokenRequest {
    pub code: String,
    pub code_verifier: String, // Random string generated by the client via PKCE
    pub redirect_uri: String,
}

#[derive(Serialize)]
pub struct TokenResponse {
    pub access_token: String,
    pub token_type: String,
    pub expires_in: u64,
}

pub async fn exchange_token(
    State(state): State<AppState>,
    Json(payload): Json<TokenRequest>,
) -> Result<Json<TokenResponse>, String> {
    // 1. Retrieve Authorization Code from DB
    let auth_record = state.db.get_auth_code(&payload.code)
        .await
        .map_err(|_| "Invalid code".to_string())?;

    // 2. PKCE Challenge Verification (Core Security Logic)
    let mut hasher = Sha256::new();
    hasher.update(payload.code_verifier.as_bytes());
    let hashed_verifier = hasher.finalize();
    let encoded_verifier = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(hashed_verifier);

    if encoded_verifier != auth_record.code_challenge {
        return Err("Code verifier mismatch".to_string());
    }

    // 3. Issue Access Token
    let access_token = generate_jwt(&state.secrets, &auth_record.user_id);

    Ok(Json(TokenResponse {
        access_token,
        token_type: "Bearer".to_string(),
        expires_in: 3600,
    }))
}

This code hashes the code_verifier sent by the client and checks if it matches the code_challenge generated when the initial authentication request was sent. This process is the core that makes ‘Zero-Touch’ secure.

Implementation Step 2: Client Side (MCP Client)

Now, the client can obtain a token without user intervention (or with minimal intervention). Let’s write an example MCP client using Python.

import hashlib
import base64
import requests
from urllib.parse import urlencode
import os # Import os module

# Zero-Touch Configuration (Hardcoded or Environment Variables)
CLIENT_ID = "zeroclaw-mcp-client"
AUTH_URL = "https://api.myblog.com/oauth/authorize"
TOKEN_URL = "https://api.myblog.com/oauth/token"
REDIRECT_URI = "http://localhost:3000/callback"

# Generate PKCE Code Verifier
random_bytes = os.urandom(32)
code_verifier = base64.urlsafe_b64encode(random_bytes).rstrip(b'=').decode('utf-8')

# Generate Code Challenge
challenge_digest = hashlib.sha256(code_verifier.encode('utf-8')).digest()
code_challenge = base64.urlsafe_b64encode(challenge_digest).rstrip(b'=').decode('utf-8')

def get_auth_url():
    params = {
        "response_type": "code",
        "client_id": CLIENT_ID,
        "redirect_uri": REDIRECT_URI,
        "scope": "mcp:read mcp:write",
        "code_challenge": code_challenge,
        "code_challenge_method": "S256"
    }
    return f"{AUTH_URL}?{urlencode(params)}"

def exchange_code_for_token(auth_code):
    data = {
        "grant_type": "authorization_code",
        "code": auth_code,
        "redirect_uri": REDIRECT_URI,
        "client_id": CLIENT_ID,
        "code_verifier": code_verifier # Sent to the server for verification
    }
    response = requests.post(TOKEN_URL, data=data)
    return response.json()

# Usage Example
# 1. User authenticates in the browser via get_auth_url() (first time)
# 2. Extract 'code' from the redirected query parameters
# 3. token = exchange_code_for_token(code)

Considerations for ZeroClaw Runtime Integration

As mentioned in our previous retrospective, ’[ZeroClaw] Q1 2026 Development Direction Meeting Minutes’, our agent runtime must prioritize security and scalability. If we integrate the OAuth flow implemented above into ZeroClaw’s Multi-Agent architecture, each agent can have an independent identity.

In particular, as discussed in ’[ZeroClaw] Multi-Agent Communication Protocol Design’, this Access Token can be used to securely access resources (APIs) of other agents during inter-agent communication, laying the foundation for secure access. This goes beyond simple blog automation and marks the first step towards a distributed system where agents trust and delegate tasks to each other.

Conclusion

Zero-Touch OAuth is an excellent approach that achieves both user convenience and security. We plan to start with small projects like blog-api-server and gradually evolve it into a core authentication module for ZeroClaw. The code above provides a basic framework, and for actual operational environments, logic such as state verification and refresh token rotation should be added.

In the next post, we will debug the ‘Handshake’ process of actually exchanging data with the MCP server using the tokens secured in this way.

Built with Hugo
Theme Stack designed by Jimmy