The Authorization Code flow uses a client_secret to authenticate when exchanging the authorization code for tokens. Use this flow when your application runs on a server and can securely store credentials — for example, Node.js, Python, or PHP backends.
Prerequisites
Before you implement this flow, ensure you have:
- OAuth2 client credentials — Register your app in Client Center to get
client_id and client_secret
- Redirect URI — Must be pre-registered in Client Center and must exactly match (protocol, host, path, trailing slashes)
- Server-side runtime — Node.js 18+, Python 3.8+, or equivalent for your language
- Session or secure storage — To persist
state between redirect and callback, and to store tokens after exchange
The client_secret is shown only once when you create the client. Copy it immediately and store it in environment variables or a secrets manager. If lost, regenerate it in Client Center.
Step 1: Build the authorization URL and redirect
Redirect the user to the Aries authorization endpoint. They will see a login and consent screen where they can approve or deny the requested permissions.
Endpoint: https://app.aries.com/oauth2/authorize
Required query parameters:
| Parameter | Description |
|---|
response_type | Must be code |
client_id | Your OAuth2 client ID |
redirect_uri | Must exactly match a registered URI |
scope | Space-separated scopes (e.g., account:information order:execution) |
state | Cryptographically random string for CSRF protection — verify on callback |
const crypto = require('crypto');
// Generate state and save to session for callback verification
const state = crypto.randomBytes(32).toString('hex');
req.session.oauth_state = state;
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.ARIES_CLIENT_ID,
redirect_uri: process.env.ARIES_REDIRECT_URI,
scope: 'account:information order:execution market:information',
state: state,
});
const authUrl = `https://app.aries.com/oauth2/authorize?${params}`;
res.redirect(authUrl);
Always generate a new, cryptographically random state for each authorization request and verify it when the user returns. This prevents CSRF attacks.
Step 2: Handle the callback
After the user approves, Aries redirects to your redirect_uri with an authorization code and the state you provided. If the user denies access, you receive error parameters instead.
Success callback:
https://yourapp.com/oauth/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE
Error callback (user denied or error):
https://yourapp.com/oauth/callback?error=access_denied&error_description=User+denied+access&state=YOUR_STATE
| Parameter | Description |
|---|
code | Authorization code — valid for 10 minutes, single-use. Exchange immediately. |
state | Must match the value you sent. Verify before proceeding. |
app.get('/oauth/callback', async (req, res) => {
// Verify state to prevent CSRF
if (req.query.state !== req.session.oauth_state) {
return res.status(400).send('Invalid state parameter');
}
// Handle user denial or other errors
if (req.query.error) {
return res.status(400).send(`Authorization error: ${req.query.error}`);
}
const authCode = req.query.code;
if (!authCode) {
return res.status(400).send('No authorization code received');
}
try {
const tokens = await exchangeCodeForToken(authCode);
req.session.access_token = tokens.access_token;
req.session.refresh_token = tokens.refresh_token;
req.session.token_expires_at = Date.now() + tokens.expires_in * 1000;
res.redirect('/dashboard');
} catch (err) {
res.status(500).send('Token exchange failed');
}
});
Step 3: Exchange the code for tokens
Send the authorization code to the token endpoint to receive an access token and refresh token. The code is single-use — exchange it immediately after receiving it.
Endpoint: POST https://api.aries.com/v1/oauth2/token
Request body:
| Field | Required | Description |
|---|
client_id | Yes | Your OAuth2 client ID |
client_secret | Yes | Your OAuth2 client secret |
grant_type | Yes | code |
code | Yes | The authorization code from the callback |
redirect_uri | Yes | Must match the redirect URI used in Step 1 |
curl -X POST 'https://api.aries.com/v1/oauth2/token' \
-H 'Content-Type: application/json' \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"grant_type": "code",
"code": "AUTHORIZATION_CODE",
"redirect_uri": "YOUR_REDIRECT_URI"
}'
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ...",
"scope": "account:information order:execution market:information"
}
Store the access_token, refresh_token, and expires_in (use it to compute expiry time). Replace your stored refresh_token if a new one is returned.
Step 4: Make authenticated API requests
Include the access token in the Authorization header for every API request.
Authorization: Bearer YOUR_ACCESS_TOKEN
curl -X GET 'https://api.aries.com/v1/users/me' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Step 5: Refresh the access token
Access tokens expire after expires_in seconds (typically 1 hour). Use the refresh token to obtain a new access token without requiring the user to log in again. Refresh proactively before expiry.
Endpoint: POST https://api.aries.com/v1/oauth2/token
Request body:
| Field | Required | Description |
|---|
client_id | Yes | Your OAuth2 client ID |
client_secret | Yes | Your OAuth2 client secret |
grant_type | Yes | refresh_token |
refresh_token | Yes | The refresh token from the initial exchange |
redirect_uri | Yes | Must match the redirect URI used in the original flow |
curl -X POST 'https://api.aries.com/v1/oauth2/token' \
-H 'Content-Type: application/json' \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"redirect_uri": "YOUR_REDIRECT_URI"
}'
The response format is the same as the initial token exchange. If a new refresh_token is returned, persist it — it replaces the previous one.
Complete example (Node.js)
Here is a minimal Express.js server demonstrating the full flow:
const express = require('express');
const crypto = require('crypto');
const session = require('express-session');
const app = express();
app.use(session({ secret: 'your-session-secret', resave: false, saveUninitialized: false }));
// Step 1: Redirect to Aries
app.get('/login', (req, res) => {
const state = crypto.randomBytes(32).toString('hex');
req.session.oauth_state = state;
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.ARIES_CLIENT_ID,
redirect_uri: process.env.ARIES_REDIRECT_URI,
scope: 'user:information account:information',
state,
});
res.redirect(`https://app.aries.com/oauth2/authorize?${params}`);
});
// Step 2 & 3: Callback and token exchange
app.get('/oauth/callback', async (req, res) => {
if (req.query.state !== req.session.oauth_state) {
return res.status(400).send('Invalid state');
}
if (req.query.error) {
return res.status(400).send(`Error: ${req.query.error}`);
}
const response = await fetch('https://api.aries.com/v1/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: process.env.ARIES_CLIENT_ID,
client_secret: process.env.ARIES_CLIENT_SECRET,
grant_type: 'code',
code: req.query.code,
redirect_uri: process.env.ARIES_REDIRECT_URI,
}),
});
if (!response.ok) {
return res.status(500).send('Token exchange failed');
}
const tokens = await response.json();
req.session.access_token = tokens.access_token;
req.session.refresh_token = tokens.refresh_token;
res.redirect('/dashboard');
});
// Step 4: Use the token
app.get('/dashboard', async (req, res) => {
if (!req.session.access_token) {
return res.redirect('/login');
}
const profile = await fetch('https://api.aries.com/v1/users/me', {
headers: { 'Authorization': `Bearer ${req.session.access_token}` },
}).then(r => r.json());
res.json(profile);
});
Next steps