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.
| Method | Used By | How It Works |
|---|---|---|
| API Key (Bearer) | Desktop / IDE clients | Static token sent in Authorization header |
| OAuth 2.1 + PKCE | Web 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:
- The Connect Wizard creates an
odoo_ai_mcp.api.keyrecord in Odoo. - The raw key is shown once and stored as a SHA-256 hash.
- The client sends
Authorization: Bearer <raw_key>on every/mcprequest. mcp_endpoint()callsodoo_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
Token Lifecycle
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 = Trueandai_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:
| Scope | Intended Use |
|---|---|
mcp | Full tool access |
mcp:read | Read-only tool access |
mcp:write | Write tool access |
Bearer Token Validation on /mcp
On every request to POST /mcp, the server:
- Extracts the
Authorization: Bearer <token>header. - Calls
odoo_ai_mcp.oauth.token.authenticate(token)— SHA-256 hash lookup inodoo_ai_mcp_oauth_token. - If no OAuth token matches, falls back to checking
odoo_ai_mcp.api.keyrecords. - If still no match, checks for an active Odoo session (
request.session.uid). - If all fail → returns JSON-RPC error
-32001(Unauthorized), HTTP 401.
[!IMPORTANT] The public user (
res.userswithid=4) is always rejected even if a valid session exists.
OAuth HTTP Routes
| Route | Method | Description |
|---|---|---|
/.well-known/oauth-authorization-server | GET | AS metadata (issuer, endpoints, PKCE, grant types) |
/.well-known/oauth-protected-resource | GET | Protected resource metadata (resource URI, scopes) |
/.well-known/oauth-protected-resource/mcp | GET | Same as above, MCP-scoped path |
/oauth/register | POST | Dynamic client registration |
/oauth/authorize | GET, POST | Consent page and code issuance |
/oauth/token | POST | Token exchange and refresh |