OS Trading Engine
API Reference
Authentication
Token Refresh

Token Refresh

When access tokens expire, use the refresh token to obtain new tokens without re-authenticating.


Endpoint

POST /api/v1/auth/refresh

Authentication: None required (refresh token in body)


Request

Headers

Content-Type: application/json

Body

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
FieldTypeRequiredDescription
refreshTokenstringYesValid refresh token from login

Response

Success (200)

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Only a new access token is returned. The same refresh token remains valid until it expires or is revoked on logout. You do not need to store a new refresh token.

Error Responses

Missing Token (400)

{
  "error": "Refresh token is required"
}

Invalid/Expired Token (401)

{
  "error": "Invalid or expired refresh token"
}

Wrong Token Type (401)

{
  "error": "Invalid token type"
}

Token Revoked (401)

{
  "error": "Refresh token has been revoked"
}

Returned when the refresh token was revoked (e.g., user logged out). Re-authenticate to obtain a new token pair.

User Not Found (401)

{
  "error": "User not found"
}

Example

cURL

curl -X POST https://your-instance.com/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

JavaScript

async function refreshTokens(refreshToken) {
  const response = await fetch('/api/v1/auth/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refreshToken }),
  });
 
  if (!response.ok) {
    // Refresh failed - redirect to login
    throw new Error('Session expired');
  }
 
  const { accessToken } = await response.json();
  
  // Store the new access token; refresh token stays the same
  storeAccessToken(accessToken);
  
  return accessToken;
}

Refresh Token Behavior

Nexgent uses reusable refresh tokens:

1. Client sends refresh token
   └─► Token is validated (signature, expiry, Redis record)

2. Server does NOT consume the refresh token
   └─► Same token remains valid until expiry or logout

3. Server issues new access token only
   └─► New access token (e.g. 15 min)
   └─► Refresh token unchanged (e.g. 24h or 30 days with "Remember Me")

4. Refresh token is revoked only on explicit logout
   └─► When client sends refresh token in logout body, it is revoked in Redis

This keeps the client simple (no need to store a new refresh token on every refresh) while still allowing immediate revocation on logout.


Handling Refresh in Your App

Proactive Refresh

Refresh before the access token expires:

// Refresh at 14 minutes (1 minute before 15-minute expiry)
const REFRESH_THRESHOLD_MS = 14 * 60 * 1000;
 
function scheduleRefresh(accessToken) {
  const payload = parseJwt(accessToken);
  const expiresAt = payload.exp * 1000;
  const refreshAt = expiresAt - REFRESH_THRESHOLD_MS;
  const delay = refreshAt - Date.now();
 
  if (delay > 0) {
    setTimeout(() => refreshTokens(), delay);
  }
}

Reactive Refresh (on 401)

Retry failed requests after refreshing:

async function apiRequest(url, options) {
  let response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${getAccessToken()}`,
    },
  });
 
  if (response.status === 401) {
    // Try to refresh
    try {
      await refreshTokens(getRefreshToken());
      
      // Retry original request with new token
      response = await fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${getAccessToken()}`,
        },
      });
    } catch {
      // Refresh failed - session expired
      redirectToLogin();
      throw new Error('Session expired');
    }
  }
 
  return response;
}

Common Issues

"Refresh token has been revoked"

Cause: The refresh token was revoked (e.g., user logged out and the client sent the refresh token in the logout request).

Solutions:

  1. Redirect to login for re-authentication
  2. Obtain a new token pair via login or registration

"Invalid or expired refresh token"

Cause: Token is malformed or expired.

Solutions:

  1. Check if token expired (e.g. 24h default, 30 days with "Remember Me")
  2. If revoked, you will see "Refresh token has been revoked" instead
  3. Redirect to login for re-authentication

Race Conditions

Multiple tabs/requests refreshing simultaneously:

let refreshPromise = null;
 
async function refreshTokens() {
  // Reuse existing refresh if in progress
  if (refreshPromise) {
    return refreshPromise;
  }
 
  refreshPromise = doRefresh();
  
  try {
    return await refreshPromise;
  } finally {
    refreshPromise = null;
  }
}