Aller au contenu

OAuth for integrators

Most users never see this: their MCP client (Claude Desktop / Code / Cursor) runs the whole flow automatically. This page is for developers building or debugging an MCP client against Aeliam.

Aeliam implements OAuth 2.1 with Dynamic Client Registration (DCR) and PKCE (S256).

Endpoints (DEV)

Role URL
MCP endpoint (Streamable HTTP, POST only) https://mcp-dev.aeliam.ai/mcp
Protected-resource metadata (RFC 9728) https://mcp-dev.aeliam.ai/.well-known/oauth-protected-resource
Authorization-server metadata (RFC 8414) https://mcp-dev.aeliam.ai/.well-known/oauth-authorization-server
Registration / authorize / token delegated to https://dev.aeliam.ai/api/v1/mcp/oauth/*

A client discovers everything from the metadata documents — no manual configuration of individual endpoints is required.

Flow

1. Discover    GET /.well-known/oauth-protected-resource  → authorization server
               GET /.well-known/oauth-authorization-server → endpoints, scopes
2. Register    POST …/oauth/register   (DCR, RFC 7591) → client_id (public, PKCE)
3. Authorize   GET  …/oauth/authorize?client_id=…&redirect_uri=…
                    &code_challenge=…&code_challenge_method=S256&scope=…&state=…
               → user logs in on dev.aeliam.ai → consent screen → Approve
4. Code        redirect_uri?code=…&state=…           (code TTL ~5 min, single-use)
5. Token       POST …/oauth/token  grant_type=authorization_code
                    &code=…&code_verifier=…&client_id=…&redirect_uri=…
               → { access_token: "aelm_mcp_at_…", refresh_token: "aelm_mcp_rt_…",
                   token_type: "Bearer", expires_in: 3600, scope: "…" }
6. Call        POST /mcp  with  Authorization: Bearer aelm_mcp_at_…
7. Refresh     POST …/oauth/token  grant_type=refresh_token  (rotating; old token revoked)
8. Revoke      POST …/oauth/revoke  (RFC 7009)

Registration (DCR)

POST https://dev.aeliam.ai/api/v1/mcp/oauth/register
{
  "client_name": "My MCP client",
  "redirect_uris": ["http://localhost:8765/cb"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

Returns a client_id (public client, no secret). Registration is rate-limited per IP.

The authorize step requires an active Aeliam session; if absent, the user is redirected to login and back (?return_to=…, same-origin only). The consent screen lists the requested scopes — read-only scopes plain, write scopes flagged (the app can modify your data). The decision is CSRF-protected (the form is HMAC-signed and re-verified server-side) and bound to the logged-in user.

Requestable scopes are the same as the REST API: crm:read, crm:write, devis:read, devis:draft, automation:read, automation:enqueue. See Authentication.

Token & transport notes

  • Access token aelm_mcp_at_…, ~1 h TTL. Refresh token aelm_mcp_rt_…, rotated on use — the previous one is revoked, so a stolen refresh token's family is detectable.
  • PKCE S256 is mandatory for public clients; the plain method is rejected.
  • The MCP endpoint is stateless Streamable HTTP: each tools/call is a POST with a JSON-RPC 2.0 body. A GET on /mcp returns 405 — that's expected.
  • Authorization codes are single-use; replay attempts revoke the issued token family.
POST https://mcp-dev.aeliam.ai/mcp
Authorization: Bearer aelm_mcp_at_…
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "whoami", "arguments": {} } }

For production, the same flow runs against https://mcp.aeliam.ai/mcp once deployed — see Environments.