Authentication
Odoo AI MCP Server

Authentication

API key and OAuth 2.1 PKCE authentication flows for MCP clients

Authentication

The MCP server supports two authentication methods. The method used depends on the AI client type.

MethodUsed ByHow It Works
API Key (Bearer)Desktop / IDE clientsStatic token sent in Authorization header
OAuth 2.1 + PKCEWeb clients (Claude.ai, ChatGPT)Browser-based authorization code flow

API Key Authentication

Desktop and IDE clients authenticate using a secret API key generated by the Connect Wizard.

How it works:

  1. The Connect Wizard creates an odoo_ai_mcp.api.key record in Odoo.
  2. The raw key is shown once and stored as a SHA-256 hash.
  3. The client sends Authorization: Bearer <raw_key> on every /mcp request.
  4. mcp_endpoint() calls odoo_ai_mcp.oauth.token.authenticate(token) which hashes the incoming value and compares against stored hashes.

Security properties:

  • Key is never stored in plaintext
  • Lost keys cannot be recovered — delete and regenerate
  • Each key is tied to a specific Odoo user

OAuth 2.1 + PKCE (Web Clients)

Web clients use the full OAuth 2.1 authorization code flow with PKCE (Proof Key for Code Exchange, S256 method).

OAuth 2.1 Flow

Rendering diagram…

Token Lifecycle

Rendering diagram…

Dynamic Client Registration

Clients that support RFC 7591-style registration can register themselves without manual setup:

curl -X POST https://your-odoo.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
    "client_name": "Claude AI",
    "grant_types": ["authorization_code"],
    "token_endpoint_auth_method": "none"
  }'

Response:

{
  "client_id": "generated-uuid",
  "client_name": "Claude AI",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "grant_types": ["authorization_code"]
}

[!NOTE] Dynamic registration requires ai_integration.oauth_enabled = True and ai_integration.oauth_advertise_discovery = True.

Scopes

The token metadata lists these scopes, though scope enforcement is not applied during tools/call in the current version:

ScopeIntended Use
mcpFull tool access
mcp:readRead-only tool access
mcp:writeWrite tool access

Bearer Token Validation on /mcp

On every request to POST /mcp, the server:

  1. Extracts the Authorization: Bearer <token> header.
  2. Calls odoo_ai_mcp.oauth.token.authenticate(token) — SHA-256 hash lookup in odoo_ai_mcp_oauth_token.
  3. If no OAuth token matches, falls back to checking odoo_ai_mcp.api.key records.
  4. If still no match, checks for an active Odoo session (request.session.uid).
  5. If all fail → returns JSON-RPC error -32001 (Unauthorized), HTTP 401.

[!IMPORTANT] The public user (res.users with id=4) is always rejected even if a valid session exists.


OAuth HTTP Routes

RouteMethodDescription
/.well-known/oauth-authorization-serverGETAS metadata (issuer, endpoints, PKCE, grant types)
/.well-known/oauth-protected-resourceGETProtected resource metadata (resource URI, scopes)
/.well-known/oauth-protected-resource/mcpGETSame as above, MCP-scoped path
/oauth/registerPOSTDynamic client registration
/oauth/authorizeGET, POSTConsent page and code issuance
/oauth/tokenPOSTToken exchange and refresh