Skip to main content

Authentication

The Cryptofuse API uses OAuth 2.0 authentication to secure access to your merchant account and ensure proper authorization for all operations.

OAuth 2.0 Overview

Cryptofuse implements OAuth 2.0 with the client credentials flow for server-to-server API integrations. This provides secure, token-based authentication without requiring user interaction.

Key Concepts

  • Client ID: Your application's unique identifier
  • Client Secret: Your application's secret key (keep this secure!)
  • Access Token: Bearer token used to authenticate API requests
  • Scopes: Permissions granted to your application (read and write)

Getting Client Credentials

To obtain OAuth2 credentials:

  1. Log in to your Cryptofuse merchant dashboard
  2. Navigate to SettingsAPI Credentials
  3. Create a new OAuth2 application
  4. Save your Client ID and Client Secret securely
Security Notice

Never expose your Client Secret in client-side code, public repositories, or insecure storage. Treat it like a password.

Client Credentials Flow

The client credentials flow is ideal for server-to-server integrations where your backend needs to access the Cryptofuse API.

Step 1: Encode Credentials

Create a Base64-encoded string of your credentials:

// Node.js
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

// Python
import base64
credentials = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()

// PHP
$credentials = base64_encode($clientId . ':' . $clientSecret);

Step 2: Request Access Token

POST /o/token/
Host: api.cryptofuse.io
Authorization: Basic {base64_encoded_credentials}
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=read write

Example with cURL:

curl -X POST https://api.cryptofuse.io/o/token/ \
-H "Authorization: Basic $(echo -n 'your_client_id:your_client_secret' | base64)" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&scope=read write"

Step 3: Token Response

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
  • access_token: Use this token for API requests
  • expires_in: Token lifetime in seconds (default: 3600 = 1 hour)
  • scope: Granted permissions

Step 4: Use Access Token

Include the access token in all API requests:

GET /payments/
Host: api.cryptofuse.io
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token Management

Token Expiration

Access tokens expire after 1 hour (3600 seconds). Implement token refresh logic:

class TokenManager {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.token = null;
this.tokenExpiry = null;
}

async getToken() {
// Check if token is still valid
if (this.token && this.tokenExpiry > Date.now()) {
return this.token;
}

// Request new token
const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
const response = await fetch('https://api.cryptofuse.io/o/token/', {
method: 'POST',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials&scope=read write'
});

const data = await response.json();
this.token = data.access_token;
this.tokenExpiry = Date.now() + (data.expires_in * 1000) - 60000; // Refresh 1 minute early

return this.token;
}
}

Revoking Tokens

To revoke an access token before expiration:

POST /o/revoke_token/
Host: api.cryptofuse.io
Authorization: Basic {base64_encoded_credentials}
Content-Type: application/x-www-form-urlencoded

token={access_token}&token_type_hint=access_token

Required Scopes

All API operations require OAuth2 scopes:

ScopeDescriptionRequired For
readRead access to resourcesGET requests, viewing data
writeWrite access to resourcesPOST, PUT, PATCH, DELETE requests

Most integrations require both scopes: scope=read write

Error Handling

Authentication Errors

401 Unauthorized

Missing or invalid credentials:

{
"error": {
"code": "authentication_failed",
"message": "Invalid authentication credentials",
"details": {
"hint": "Check your client_id and client_secret"
}
}
}

403 Forbidden

Invalid or missing scopes:

{
"error": {
"code": "permission_denied",
"message": "Token does not have required scope",
"details": {
"required_scopes": ["read", "write"],
"token_scopes": ["read"]
}
}
}

Invalid Grant

Incorrect credentials or grant type:

{
"error": "invalid_grant",
"error_description": "Invalid credentials given."
}

Common Issues and Solutions

IssueSolution
"Invalid client" errorVerify client_id and client_secret are correct
"Unsupported grant type"Ensure using grant_type=client_credentials
Token expired quicklyCheck server time synchronization
CORS errorsOAuth2 endpoints don't support CORS (server-side only)

Security Best Practices

1. Secure Credential Storage

# Good: Use environment variables
import os
CLIENT_ID = os.environ.get('CRYPTOFUSE_CLIENT_ID')
CLIENT_SECRET = os.environ.get('CRYPTOFUSE_CLIENT_SECRET')

# Bad: Hardcoded credentials
CLIENT_ID = "abc123" # Never do this!
CLIENT_SECRET = "secret123" # Never do this!

2. Server-Side Only

Never use OAuth2 client credentials in:

  • Frontend JavaScript
  • Mobile applications
  • Public repositories
  • Client-side code

3. Token Security

  • Store tokens in memory, not persistent storage
  • Use HTTPS for all API calls
  • Implement proper token refresh logic
  • Log token usage for security auditing

4. Rate Limiting

OAuth2 endpoints have specific rate limits:

  • /o/token/: 20 requests per minute per IP
  • API endpoints: 1000 requests per hour per token

Rate limit headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704445200

Implementation Examples

Python (requests)

import requests
import base64
from datetime import datetime, timedelta

class CryptofuseAuth:
def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret
self.token = None
self.token_expires = None
self.base_url = "https://api.cryptofuse.io"

def get_token(self):
if self.token and self.token_expires > datetime.now():
return self.token

credentials = base64.b64encode(
f"{self.client_id}:{self.client_secret}".encode()
).decode()

response = requests.post(
f"{self.base_url}/o/token/",
headers={
"Authorization": f"Basic {credentials}",
"Content-Type": "application/x-www-form-urlencoded"
},
data="grant_type=client_credentials&scope=read write"
)

if response.status_code == 200:
data = response.json()
self.token = data["access_token"]
self.token_expires = datetime.now() + timedelta(seconds=data["expires_in"] - 60)
return self.token
else:
raise Exception(f"Auth failed: {response.text}")

def make_request(self, method, endpoint, **kwargs):
token = self.get_token()
headers = kwargs.get("headers", {})
headers["Authorization"] = f"Bearer {token}"
kwargs["headers"] = headers

return requests.request(method, f"{self.base_url}{endpoint}", **kwargs)

Node.js (axios)

const axios = require('axios');

class CryptofuseAuth {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.token = null;
this.tokenExpires = null;
this.baseURL = 'https://api.cryptofuse.io';
}

async getToken() {
if (this.token && this.tokenExpires > Date.now()) {
return this.token;
}

const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');

try {
const response = await axios.post(
`${this.baseURL}/o/token/`,
'grant_type=client_credentials&scope=read write',
{
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

this.token = response.data.access_token;
this.tokenExpires = Date.now() + ((response.data.expires_in - 60) * 1000);

return this.token;
} catch (error) {
throw new Error(`Authentication failed: ${error.response?.data?.error || error.message}`);
}
}

async request(method, endpoint, data = null) {
const token = await this.getToken();

return axios({
method,
url: `${this.baseURL}${endpoint}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data
});
}
}

// Usage
const client = new CryptofuseAuth(process.env.CLIENT_ID, process.env.CLIENT_SECRET);
const payments = await client.request('GET', '/payments/');

Testing Authentication

Verify your authentication is working:

# Get token
TOKEN=$(curl -s -X POST https://api.cryptofuse.io/o/token/ \
-H "Authorization: Basic $(echo -n 'your_client_id:your_client_secret' | base64)" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&scope=read write" \
| jq -r '.access_token')

# Test with a simple request
curl -X GET https://api.cryptofuse.io/user/me/ \
-H "Authorization: Bearer $TOKEN"

Expected response:

{
"name": "Your Merchant Name",
"schema_name": "tenant_yourname",
"created_on": "2025-01-01T00:00:00Z"
}

Support

Having authentication issues? Contact us:

  • Email: support@cryptofuse.io
  • Include your Client ID (never send Client Secret)
  • Describe the error message and HTTP status code