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.
Consent & scopes¶
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 tokenaelm_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
plainmethod is rejected. - The MCP endpoint is stateless Streamable HTTP: each
tools/callis aPOSTwith a JSON-RPC 2.0 body. AGETon/mcpreturns405— 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.