OAuth reference
modelux runs an OAuth 2.1 authorization server that issues tokens to MCP
clients (Claude Desktop, Claude Code, ChatGPT, Cursor, and anything that
speaks the MCP authorization spec).
You do not need to call these endpoints by hand — your MCP client discovers
and drives them automatically after you paste https://api.modelux.ai/api/mcp
as the server URL. This page documents the surface for client developers and
for anyone debugging a connection.
For the end-user setup walkthrough, see Connecting your AI (MCP).
Metadata
GET /.well-known/oauth-protected-resource
RFC 9728. Tells clients
which authorization server mints tokens for /api/mcp.
{
"resource": "https://api.modelux.ai/api/mcp",
"authorization_servers": ["https://api.modelux.ai"],
"scopes_supported": ["mcp:read", "mcp:write"],
"bearer_methods_supported": ["header"]
}
GET /.well-known/oauth-authorization-server
RFC 8414. Advertises the authorization server’s endpoints and capabilities.
Highlights: code_challenge_methods_supported: ["S256"] (PKCE is required,
plain is rejected), token_endpoint_auth_methods_supported: ["none"] (all
clients are public), grant_types_supported: ["authorization_code", "refresh_token"].
Endpoints
POST /api/oauth/register — Dynamic Client Registration
RFC 7591. Register a new
public client. JSON body; returns a client_id you use on subsequent
authorize / token calls. Clients are public (no client_secret) and must
use PKCE.
curl -sS https://api.modelux.ai/api/oauth/register \
-H 'content-type: application/json' \
-d '{
"client_name": "My MCP Client",
"redirect_uris": ["http://127.0.0.1:8787/callback"]
}'
Redirect URIs must be HTTPS, except http://localhost and http://127.0.0.1
for loopback clients. Fragments are rejected.
GET /api/oauth/authorize — Authorization
User-facing browser endpoint. Your MCP client opens this URL with:
| Param | Required | Notes |
|---|---|---|
client_id | yes | from /register (or a pre-registered slug) |
redirect_uri | yes | must exact-match a URI registered for the client |
response_type | yes | must be code |
code_challenge | yes | PKCE RFC 7636 |
code_challenge_method | yes | must be S256 |
scope | yes | space-separated subset of mcp:read mcp:write |
state | recommended | echoed back on redirect |
resource | yes | must be https://api.modelux.ai/api/mcp (RFC 8707) |
Unauthenticated users are bounced to /login and returned here after
sign-in. After consent, the browser is redirected to
<redirect_uri>?code=<code>&state=<state>.
POST /api/oauth/token
Form-encoded body. Two grants:
authorization_code — exchange a code + PKCE verifier for an access
token and refresh token.
curl -sS https://api.modelux.ai/api/oauth/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=mlx_oac_...' \
-d 'client_id=<your-client-id>' \
-d 'redirect_uri=http://127.0.0.1:8787/callback' \
-d 'code_verifier=<pkce-verifier>' \
-d 'resource=https://api.modelux.ai/api/mcp'
refresh_token — rotate a refresh token.
curl -sS https://api.modelux.ai/api/oauth/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=refresh_token' \
-d 'refresh_token=mlx_ort_...' \
-d 'client_id=<your-client-id>'
Response:
{
"access_token": "mlx_oat_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "mlx_ort_...",
"scope": "mcp:read mcp:write"
}
Refresh tokens rotate on every use. Presenting a rotated (already-replaced)
refresh token revokes the whole chain and returns invalid_grant — treat
that as “re-run the authorize flow.”
POST /api/oauth/revoke
RFC 7009. Revoke either an access or refresh token. Unknown tokens return 200 per spec.
curl -sS -X POST https://api.modelux.ai/api/oauth/revoke \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'token=mlx_oat_...'
Using a token against MCP
Every MCP JSON-RPC call presents the access token as a bearer:
POST /api/mcp
Authorization: Bearer mlx_oat_...
/api/mcp validates the audience (resource) binding, checks the token
hasn’t expired or been revoked, and verifies the token owner is still a
member of their org. Failures return 401 with a WWW-Authenticate header
pointing at /.well-known/oauth-protected-resource so spec-compliant
clients restart the auth dance without user intervention.
Token lifetimes
| Token | TTL | Notes |
|---|---|---|
| Authorization code | 10 min | single-use |
| Access token | 1 h | opaque, not JWT; revoke is instant |
| Refresh token | 30 d | rotates on every use; reuse detection revokes the chain |
Scopes
mcp:read— list / get / describe tools only.mcp:write— required for any mutating tool (create, update, delete, revoke, …).
Scope narrows the token; the underlying dashboard role still gates the
action. A user whose dashboard role is viewer cannot escalate by
requesting mcp:write.