If your app needs to call a user's Ace Data Cloud resources, asking them to copy an API key is usually the worst part of the onboarding flow.
A cleaner approach is OAuth 2.0 Authorization Code Flow with PKCE: the user clicks a login button, approves the requested scopes, and your app receives an access token that can call only the resources the user authorized.
This guide walks through the practical shape of that integration using the public Ace Data Cloud OAuth documentation.
What you can build
The OAuth flow is meant for third-party applications, agents, MCP clients, and automation workflows that want a user to sign in with Ace Data Cloud and then access platform resources on that user's behalf.
The auth base URL is:
https://auth.acedata.cloud
The key endpoints are:
| Purpose | Endpoint |
|---|---|
| Discovery document | GET /.well-known/oauth-authorization-server |
| Browser authorization | GET https://auth.acedata.cloud/oauth2/authorize |
| Token exchange or refresh | POST https://auth.acedata.cloud/oauth2/token |
| Revoke token | POST https://auth.acedata.cloud/oauth2/revoke |
| User information | GET https://auth.acedata.cloud/api/v1/users/me |
| OAuth app management | https://auth.acedata.cloud/user/oauth-apps |
You can fetch the current discovery document with:
curl https://auth.acedata.cloud/.well-known/oauth-authorization-server
Supported capabilities include response_type=code, grant_types=authorization_code, refresh_token, PKCE challenge methods S256 and plain, and client authentication methods client_secret_post for confidential clients or none for public PKCE clients.
Choose scopes with least privilege
Scopes are what make the OAuth flow safer than a copied API key. The user sees the permissions you request, and your token is limited to those permissions.
For identity, the supported scopes include:
-
openid: returns the user's uniqueid -
profile: returns fields such asusername,nickname,avatar,is_verified, anddate_joined -
email: returnsemail -
phone: returnsphoneandregion
For platform resources, the documented scopes include:
-
applications:readandapplications:write -
credentials:readandcredentials:write usage:read-
orders:readandorders:write
There are also aggregate scopes. platform:read expands to applications:read, credentials:read, usage:read, and orders:read. platform:write expands to applications:write, credentials:write, and orders:write. platform expands to both read and write groups.
A minimal sign-in flow might request only openid profile. An MCP or IDE client that needs to configure keys could request openid profile credentials:read credentials:write. Request offline_access only if you need a refresh token.
Register the OAuth application
Create the application at:
https://auth.acedata.cloud/user/oauth-apps
During registration, choose a client type:
-
Confidential: a backend service that can storeclient_secret -
Public: frontend, desktop, CLI, or mobile clients that cannot store secrets and must use PKCE
You also configure redirect URIs. The redirect URI must exactly match the redirect_uri you send later in the authorization request. After saving, you receive a client_id. Confidential clients also see a client_secret once, so save it immediately.
Redirect the user to authorize
The browser redirect starts at:
https://auth.acedata.cloud/oauth2/authorize
A typical authorization URL looks like this:
https://auth.acedata.cloud/oauth2/authorize
?response_type=code
&client_id=<your client_id>
&redirect_uri=<your registered callback address>
&scope=openid%20profile%20credentials:read
&state=<random CSRF protection string>
&code_challenge=<PKCE challenge value>
&code_challenge_method=S256
Always validate state when the user returns to your redirect URI. It is your CSRF protection value.
For PKCE, generate a random code_verifier, then compute:
code_challenge = BASE64URL(SHA256(code_verifier))
Send the code_challenge in the authorization URL and keep the code_verifier for the token exchange. After approval, the browser returns to:
<redirect_uri>?code=<authorization code>&state=<state returned as is>
If the user denies access, the redirect contains error=access_denied and an error_description.
The authorization code is valid for 10 minutes and can be used only once, so exchange it promptly.
Exchange the code for tokens
Confidential clients exchange the code with client_secret:
curl -X POST https://auth.acedata.cloud/oauth2/token \
-d grant_type=authorization_code \
-d code=<code obtained in the previous step> \
-d client_id=<your client_id> \
-d client_secret=<your client_secret> \
-d redirect_uri=<callback address that exactly matches Step 2>
Public PKCE clients exchange the code with code_verifier instead:
curl -X POST https://auth.acedata.cloud/oauth2/token \
-d grant_type=authorization_code \
-d code=<code> \
-d client_id=<your client_id> \
-d code_verifier=<code_verifier generated earlier> \
-d redirect_uri=<callback address>
A successful response has this shape:
{
"access_token": "<JWT>",
"token_type": "Bearer",
"expires_in": 1296000,
"scope": "openid profile credentials:read",
"refresh_token": "<JWT, only when offline_access>"
}
The access token is a JWT with scope claims and is valid for 15 days. The refresh token appears only when offline_access was requested and is valid for 30 days.
Call APIs with the access token
To read user information, send the token in the Authorization: Bearer header:
curl https://auth.acedata.cloud/api/v1/users/me \
-H "Authorization: Bearer <access_token>"
The returned fields depend on the identity scopes the user granted.
For platform resource APIs, call api.acedata.cloud with the same bearer token. For example, if credentials:read was granted:
curl https://api.acedata.cloud/api/v1/credentials/ \
-H "Authorization: Bearer <access_token>"
The platform backend validates the JWT scopes. If the token tries to access an unauthorized resource, the API returns 403.
Refresh and revoke tokens
If you requested offline_access, refresh an expired access token with:
curl -X POST https://auth.acedata.cloud/oauth2/token \
-d grant_type=refresh_token \
-d refresh_token=<your refresh_token>
The refresh token is rotated after refresh, so store the new one. To revoke an access or refresh token:
curl -X POST https://auth.acedata.cloud/oauth2/revoke \
-d token=<access_token or refresh_token>
Handle errors deliberately
OAuth errors use this format:
{
"error": "<code>",
"error_description": "<human-readable explanation>"
}
Common cases include invalid_request, invalid_client, invalid_grant, access_denied, and unsupported_grant_type. In practice, most integration bugs are invalid_grant: an expired code, a reused code, PKCE verification failure, or a redirect_uri mismatch.
The docs also note some useful limits: each account can create up to 20 OAuth applications, authorization codes are valid for 10 minutes and single use, access tokens are valid for 15 days, refresh tokens are valid for 30 days and rotated, and redirect_uri must match exactly.
If you are building an agent, MCP client, or internal dashboard, this flow gives users a familiar login experience while keeping access scoped and auditable. The original reference is here: https://platform.acedata.cloud/documents/oauth-integration
Top comments (0)