Przejdź do głównej zawartości

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 Settings → API 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: <your-api-host>
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 {{BASE_URL}}/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: <your-api-host>
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('{{BASE_URL}}/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: <your-api-host>
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 = "{{BASE_URL}}"

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 = '{{BASE_URL}}';
}

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 {{BASE_URL}}/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 {{BASE_URL}}/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