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 (
readandwrite)
Getting Client Credentials
To obtain OAuth2 credentials:
- Log in to your Cryptofuse merchant dashboard
- Navigate to Settings → API Credentials
- Create a new OAuth2 application
- Save your Client ID and Client Secret securely
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 requestsexpires_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:
| Scope | Description | Required For |
|---|---|---|
read | Read access to resources | GET requests, viewing data |
write | Write access to resources | POST, 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
| Issue | Solution |
|---|---|
| "Invalid client" error | Verify client_id and client_secret are correct |
| "Unsupported grant type" | Ensure using grant_type=client_credentials |
| Token expired quickly | Check server time synchronization |
| CORS errors | OAuth2 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: [email protected]
- Include your Client ID (never send Client Secret)
- Describe the error message and HTTP status code