Skip to content

OAuth 2.1

SuperSpace runs a standard OAuth 2.1 authorization server. Use it for third-party apps acting on a user's behalf; use API keys for first-party / server-to-server integrations.

OAuth access tokens are never admin and are fail-closed: an endpoint that does not declare a scope is unavailable via OAuth, and a token missing a required scope is rejected. Session and API-key credentials bypass scope checks entirely.


Authorization Server Metadata

Metadata is per-brand, derived from the request host.

GET /.well-known/oauth-authorization-server

Returned Params
  • issuer: String
  • authorization_endpoint: String | https://<host>/oauth/authorize
  • token_endpoint: String | https://<host>/oauth/token
  • revocation_endpoint: String | https://<host>/oauth/revoke
  • introspection_endpoint: String | https://<host>/oauth/introspect
  • registration_endpoint: String | https://<host>/oauth/registration
  • response_types_supported: Array | ["code"]
  • grant_types_supported: Array | ["authorization_code", "refresh_token"]
  • code_challenge_methods_supported: Array | ["S256"]
  • token_endpoint_auth_methods_supported: Array | ["client_secret_basic", "client_secret_post", "none"]
  • scopes_supported: Array | see Scopes
  • service_documentation: String

Flows

  • Grant types: authorization_code and refresh_token only.
  • PKCE is mandatory (S256 only).
  • Refresh tokens rotate — the previous refresh token is revoked on use. Rotation only advances once the new access token is validated against the brand, trial, and role checks.
  • No OpenID Connect — there is no /.well-known/openid-configuration, no userinfo, no ID tokens. The OIDC gem is wired only for Dynamic Client Registration.
  • Brand isolation — a token is bound to the brand (hostname) it was issued under and is rejected on any other brand. The authorize screen only lists the user's non-trial accounts on the current brand.

Token revocation on role change

Removing a user's role on an account revokes their OAuth tokens (and sessions) for that account.


Dynamic Client Registration (DCR)

POST /oauth/registration (RFC 7591)

Only public PKCE clients are allowed — token_endpoint_auth_method must be "none".

Errors
  • 400 {"error":"invalid_client_metadata", ...} | token_endpoint_auth_method is not "none"
  • 429 {"error":"too_many_requests", ...} | per-IP limit of 50 registrations / hour exceeded

Scopes

There are no default scopes — token presence is identity, and every resource scope is opt-in.

Scope Grants
sites:read Site reads + nested site reads (show/index, backups list, cache status, CDN status, variants list, tasks, edge-rules list, Shield reads, logs, metrics)
sites:write Site + nested site writes (create/update/destroy, backups, cache, restart, restore, edge rules, Shield writes, certificates, site-domain CRUD, variant change)
domains:read Domains list/show/query/available; domain-registration index/show/check/suggestions; contacts/hosts/processes reads
domains:write Domain-registration mutations; domain-contact create/update/destroy/resend; host/process writes
dns:read DNS zones index/show/dns_stats; DNS records index/show
dns:write DNS zone & record create/update/destroy
billing:read Orders index/show; subscriptions index/show; carts show

GET /api/about accepts any valid token regardless of scope.

Endpoints Unavailable via OAuth

These declare no scope and are fail-closed — they require a session or API-key credential and return 403 endpoint not available via OAuth for any OAuth token:

  • Account CRUD & account roles
  • API keys
  • Users and user_roles
  • The global tasks endpoints (GET /api/tasks/:id)
  • SSO (per-site and top-level)
  • Task result reporting (POST /api/webhooks/task/{task-id})
  • The domain-order endpoints: POST /api/orders/domain and POST /api/domain_registrations/:id/registrant_change (both place a paid order and require a user-scoped API key — registrant_change is deliberately excluded from the domains:write scope)
  • All write operations on orders/sites-billing (there is intentionally no billing:write scope)

Scope Enforcement Errors

Errors
  • 401 {"error":"invalid_token","error_description":"token audience is not valid for /api"} | the token carries a resource audience (RFC 8707) minted for another resource (e.g. the MCP server) — /api rejects it before the scope check
  • 403 {"error":"insufficient_scope","error_description":"requires scope: <required>"} | with header WWW-Authenticate: Bearer error="insufficient_scope", scope="<required>"
  • 403 {"error":"insufficient_scope","error_description":"endpoint not available via OAuth"} | endpoint declares no scope