v0.2.1 — OAuth 2.1 + per-user identity
v0.2.1 — OAuth 2.1 + per-user identity
This release replaces the static MCP_BEARER_TOKEN of v0.2.0 with a full
OAuth 2.1 Authorization Code + PKCE (S256) flow. Each end-user authenticates
with their own Odoo credentials, receives their own access token, and tool
calls execute under their per-user OdooClient.
stdio mode is unchanged. All v0.2.0 stdio deployments continue to work
without any new env vars.
Highlights
- OAuth 2.1 Authorization Code + PKCE (S256) replaces the single shared
bearer token. - Per-user identity end-to-end: tool calls execute under each user's
own Odoo credentials;user_idflows throughAsyncLocalStoragefrom
HTTP request → tool handler. - Admin API (
/admin/users) for managing the allowlist, with admin
password authentication and 5/IP/60s rate limit. - Encrypted credential store (AES-256-GCM, fresh IV per record,
chmod 0o600). Tokens stored only as SHA-256 hashes. - CSRF protection on the consent form (
HttpOnly; SameSite=Strict
cookie + hidden form token, validated withtimingSafeEqual). - Bundled CLI subcommands:
odoo-mcp users {list|allow|revoke}and
odoo-mcp auth <url>for the manual OAuth dance. - Security posture: 99/100 (1 deferred LOW finding, see below).
User-facing changes
Required env vars (HTTP mode only)
MCP_ENCRYPTION_KEY=$(openssl rand -base64 32)
MCP_ADMIN_PASSWORD=$(openssl rand -hex 32)
MCP_USER_STORE_PATH=/var/lib/odoo-mcp/users.json
MCP_PUBLIC_URL=https://mcp.example.com # optional, for TLS proxiesRemoved env vars
MCP_BEARER_TOKEN— silently ignored. Remove from your deployment when
you upgrade.
Stdio mode
Unchanged. ODOO_URL / ODOO_DB / ODOO_USERNAME / ODOO_API_KEY work
exactly as in v0.2.0. None of the new vars are required.
End-user flows
- Claude Desktop / Cursor / DCR-capable clients: fully automatic. The
client discovers/.well-known/oauth-authorization-server, does DCR,
opens the consent page in a browser, exchanges the code for a token,
uses the token for/mcpcalls. - Claude Code (manual bearer): run
odoo-mcp auth <server-url>from a
terminal, complete the consent form in the browser, copy the printed
token intoclaude mcp add --header "Authorization: Bearer <token>".
See docs/v0.2.1-oauth.md for the full migration guide.
Breaking changes
| Change | Migration path |
|---|---|
MCP_BEARER_TOKEN removed from /mcp auth |
Set the 4 new HTTP-mode env vars; allow users with odoo-mcp users allow; each user runs odoo-mcp auth to issue their token. |
HttpTransportConfig.bearerToken field removed |
Update any custom callers of startHttpTransport to pass oauthEndpoints, adminEndpoints, userStore, clientCache instead. |
registerAllTools(server, client, session, logger) signature → registerAllTools(server, clientResolver, logger) |
Tool handler call sites resolve client/session at invocation time via clientResolver() instead of at registration time. |
createOdooMcpServer accepts optional clientResolver in McpServerConfig, returns probeClient in addition to existing fields |
Stdio callers need no change (backward-compat additions). HTTP callers must build a clientResolver (see bin.ts:81–99 for the canonical implementation). |
Stdio-mode end users have no breaking changes.
Deployment checklist
Pre-deployment
- Generate the 4 new secrets (encryption key, admin password, user store
path, public URL if behind a TLS proxy). Store the encryption key
somewhere recoverable — rotating it destroys all user records. - Ensure the
MCP_USER_STORE_PATHparent directory exists and is writable
by the server user. - Verify reverse-proxy headers if behind a TLS terminator:
MCP_TRUST_PROXY=1
enablesX-Forwarded-For/X-Forwarded-Protoparsing. - Pre-allowlist users you'll onboard immediately:
odoo-mcp users allow <email> --url <server-url>.
During deployment
- Stop v0.2.0 servers.
- Push v0.2.1 image / binary.
- Set the new env vars in your secret store.
- Start v0.2.1 servers.
- Verify
/healthreturns 200 andprobe_ok: true. - Verify
/.well-known/oauth-authorization-serverreturns the 8 metadata
fields.
Post-deployment
- Notify users: each must run
odoo-mcp auth <url>(or reconnect their
DCR-capable client) to obtain a new access token. - Confirm token issuance is logged (
{"event":"token_issued","email":...}). - Monitor rate-limit observables:
auth_failure_count,dcrRateMapsize.
Database migrations
None. The user store is a flat JSON file written atomically on every mutation.
First boot creates the file at MCP_USER_STORE_PATH with chmod 0o600.
Rollback plan
- Stop the v0.2.1 servers.
- Restore the v0.2.0 binary or image.
- Restore the v0.2.0 env vars (
MCP_BEARER_TOKENetc.). - Start v0.2.0 servers.
The v0.2.1 user store file (MCP_USER_STORE_PATH) is forward-only —
v0.2.0 doesn't read it. No data conversion needed in either direction.
Issued OAuth tokens are not honored by v0.2.0, so users will fall back to
the static bearer flow on rollback (re-configure clients accordingly).
Security
| Item | Status |
|---|---|
| Posture score (last audit 2026-05-18) | 99 / 100 |
| CRITICAL findings | 0 |
| HIGH findings | 0 |
| MEDIUM findings | 0 (3 resolved this cycle: F-001, F-002, F-003) |
| LOW findings | 1 (F-004 resolved; F-005 deferred — see evidence/security-audit.json) |
| Threat-model coverage | 15/15 [threat-model] criteria FULL |
Deferred for future work: F-005 — resolveToken() linear scan in
user-store.ts (timing side-channel, confidence 7, below daily audit gate).
Mitigations already in place: tokens are SHA-256-hashed so comparison
target is opaque; per-user token cap of 10 bounds total tokens; network
jitter dominates the timing signal.
Reproducibility manifest
| Field | Value |
|---|---|
| Spec name | odoo-mcp-oauth |
| Plugin version | spec-engine 2.3.0 |
| git_sha_start | cc3383046342275f28edf7c855f41798a65cd999 |
| git_sha_release | 9004325 (tip of v0.2) |
| Tasks completed | 21 / 21 |
| Waves | 7 |
| Test count at release | 422 unit + 3 stdio regression + 10-step OAuth dance integration |
| Tool stack | TypeScript 5.7, Node.js 22+, pnpm 9, Biome 1.9, Vitest 3 |
| Model used | Claude Opus 4.7 (1M context) |
Documentation
End-user migration guide:
Operator + integrator + contributor reference (under
.claude/specs/odoo-mcp-oauth/docs/):
README.mdarchitecture.md— module map, ClientResolver patternoperator-guide.md— config, file format, monitoring, rotationoauth-flow.md— endpoint reference, CSRF mechanics, PKCE bytestools-reference.md— the 9 tools, schemas, error contractsecurity-posture.md— STRIDE coverage map, F-005 statuscli-reference.md—users+authsubcommands
Acknowledgements
OAuth and admin endpoints implemented per the threat model in
evidence/threat-model.md. Spec, execution, acceptance, and audit
artifacts are preserved under .claude/specs/odoo-mcp-oauth/ for
auditability and reproducibility.