Skip to content

v0.2.1 — OAuth 2.1 + per-user identity

Choose a tag to compare

@farshidghyasi farshidghyasi released this 17 May 23:28
· 7 commits to v0.2 since this release

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_id flows through AsyncLocalStorage from
    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 with timingSafeEqual).
  • 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 proxies

Removed 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 /mcp calls.
  • Claude Code (manual bearer): run odoo-mcp auth <server-url> from a
    terminal, complete the consent form in the browser, copy the printed
    token into claude 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_PATH parent directory exists and is writable
    by the server user.
  • Verify reverse-proxy headers if behind a TLS terminator: MCP_TRUST_PROXY=1
    enables X-Forwarded-For / X-Forwarded-Proto parsing.
  • 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 /health returns 200 and probe_ok: true.
  • Verify /.well-known/oauth-authorization-server returns 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, dcrRateMap size.

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

  1. Stop the v0.2.1 servers.
  2. Restore the v0.2.0 binary or image.
  3. Restore the v0.2.0 env vars (MCP_BEARER_TOKEN etc.).
  4. 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/):


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.