SDK Authentication
This guide covers authentication methods for Staqr and Commerce APIs, including token management, OAuth flows, and security best practices.
Authentication Overview
Staqr uses different authentication methods depending on the API:
| API | Auth Method | Token Type | Typical Use |
|---|---|---|---|
| Staqr Platform | JWT | Bearer Token | Platform operations |
| Commerce Direct | Keycloak OAuth2 | Access Token | Billing/CRM operations |
| Commerce via Staqr Proxy | Staqr JWT | Bearer Token | Simplified Commerce access |
Staqr Platform API
Obtaining API Keys
- Log in to your Staqr instance
- Navigate to Settings > API Keys
- Click Create New Key
- Copy the key (shown only once)
- Store securely in environment variable
Using API Keys
TypeScript:
import { Configuration } from '@staqr/staqr-api';
const config = new Configuration({
basePath: 'https://my.staqr.com/api/v1',
accessToken: process.env.STAQR_API_KEY
});
Python:
from staqr import Configuration
config = Configuration()
config.host = "https://my.staqr.com/api/v1"
config.access_token = os.environ.get("STAQR_API_KEY")
Commerce API (Direct)
Commerce API uses Keycloak OAuth2 for authentication.
Getting OAuth Tokens
Step 1: Obtain Token from Keycloak
curl -X POST "https://auth.staqr.com/realms/staqr/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=commerce" \
-d "username=YOUR_USERNAME" \
-d "password=YOUR_PASSWORD"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer"
}
Using Commerce Tokens
TypeScript:
import { Configuration, CustomerManagementApi } from '@staqr/commerce-api-v1';
// Token obtained from Keycloak
const commerceToken = await getKeycloakToken();
const config = new Configuration({
basePath: 'https://commerce.staqr.com/api',
accessToken: commerceToken,
headers: {
'X-Tenant': 'PRVIDR' // Required for multi-tenant
}
});
const api = new CustomerManagementApi(config);
Python:
import requests
from staqr_commerce_v1 import Configuration, ApiClient, CustomerManagementApi
def get_keycloak_token():
response = requests.post(
"https://auth.staqr.com/realms/staqr/protocol/openid-connect/token",
data={
"grant_type": "password",
"client_id": "commerce",
"username": os.environ.get("COMMERCE_USERNAME"),
"password": os.environ.get("COMMERCE_PASSWORD")
}
)
return response.json()["access_token"]
config = Configuration()
config.host = "https://commerce.staqr.com/api"
config.access_token = get_keycloak_token()
config.default_headers = {"X-Tenant": "PRVIDR"}
with ApiClient(config) as client:
api = CustomerManagementApi(client)
Commerce via Staqr Proxy
The simpler approach: Use Staqr JWT to access Commerce through the proxy.
Benefit: Single authentication for both Staqr and Commerce APIs.
TypeScript:
import { Configuration } from '@staqr/commerce-api-v1';
// Use Staqr JWT (not Keycloak token)
const config = new Configuration({
basePath: 'https://my.staqr.com/api/commerce', // Proxy endpoint
accessToken: process.env.STAQR_API_KEY,
headers: {
'x-seller-id': 'PRVIDR'
}
});
Python:
config = Configuration()
config.host = "https://my.staqr.com/api/commerce" # Proxy endpoint
config.access_token = os.environ.get("STAQR_API_KEY")
config.default_headers = {"x-seller-id": "PRVIDR"}
Token Refresh
Automatic Token Refresh
For long-running applications, implement automatic token refresh:
TypeScript:
class TokenManager {
private accessToken: string | null = null;
private refreshToken: string | null = null;
private expiresAt: number = 0;
async getToken(): Promise<string> {
if (this.accessToken && Date.now() < this.expiresAt - 30000) {
return this.accessToken;
}
if (this.refreshToken) {
await this.refreshAccessToken();
} else {
await this.authenticate();
}
return this.accessToken!;
}
private async authenticate(): Promise<void> {
const response = await fetch(
'https://auth.staqr.com/realms/staqr/protocol/openid-connect/token',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'password',
client_id: 'commerce',
username: process.env.COMMERCE_USERNAME!,
password: process.env.COMMERCE_PASSWORD!
})
}
);
const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
this.expiresAt = Date.now() + (data.expires_in * 1000);
}
private async refreshAccessToken(): Promise<void> {
const response = await fetch(
'https://auth.staqr.com/realms/staqr/protocol/openid-connect/token',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'commerce',
refresh_token: this.refreshToken!
})
}
);
const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
this.expiresAt = Date.now() + (data.expires_in * 1000);
}
}
// Usage with SDK
const tokenManager = new TokenManager();
const config = new Configuration({
basePath: 'https://commerce.staqr.com/api',
accessToken: async () => tokenManager.getToken()
});
Python:
import time
import requests
import threading
class TokenManager:
def __init__(self):
self.access_token = None
self.refresh_token = None
self.expires_at = 0
self.lock = threading.Lock()
def get_token(self) -> str:
with self.lock:
if self.access_token and time.time() < self.expires_at - 30:
return self.access_token
if self.refresh_token:
self._refresh_access_token()
else:
self._authenticate()
return self.access_token
def _authenticate(self):
response = requests.post(
"https://auth.staqr.com/realms/staqr/protocol/openid-connect/token",
data={
"grant_type": "password",
"client_id": "commerce",
"username": os.environ.get("COMMERCE_USERNAME"),
"password": os.environ.get("COMMERCE_PASSWORD")
}
)
data = response.json()
self.access_token = data["access_token"]
self.refresh_token = data["refresh_token"]
self.expires_at = time.time() + data["expires_in"]
def _refresh_access_token(self):
response = requests.post(
"https://auth.staqr.com/realms/staqr/protocol/openid-connect/token",
data={
"grant_type": "refresh_token",
"client_id": "commerce",
"refresh_token": self.refresh_token
}
)
data = response.json()
self.access_token = data["access_token"]
self.refresh_token = data["refresh_token"]
self.expires_at = time.time() + data["expires_in"]
# Usage
token_manager = TokenManager()
config.access_token = token_manager.get_token()
Security Best Practices
1. Never Hardcode Credentials
// NEVER DO THIS
const config = new Configuration({
accessToken: 'YOUR_API_KEY_HERE' // Exposed in code!
});
// Use environment variables
const config = new Configuration({
accessToken: process.env.STAQR_API_KEY
});
2. Use Secret Managers in Production
AWS Secrets Manager:
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const secretsManager = new SecretsManager({ region: 'ap-southeast-2' });
async function getApiKey(): Promise<string> {
const secret = await secretsManager.getSecretValue({
SecretId: 'staqr/api-key'
});
return secret.SecretString!;
}
const config = new Configuration({
accessToken: await getApiKey()
});
3. Rotate Keys Regularly
- Rotate production API keys every 90 days
- Revoke keys immediately when team members leave
- Use separate keys for different environments
4. Use Least Privilege
- Create keys with minimum required permissions
- Don't share admin keys with application code
- Audit key usage regularly
5. Secure Key Storage
| Environment | Storage Method |
|---|---|
| Local Dev | .env file (gitignored) |
| CI/CD | Pipeline secrets |
| Production | Secret manager (AWS/GCP/Azure) |
| Mobile | Backend proxy (never in app) |
Environment Variables
Required Variables
# Staqr Platform
STAQR_API_KEY=your-staqr-api-key
STAQR_BASE_URL=https://my.staqr.com/api/v1
# Commerce (direct)
COMMERCE_BASE_URL=https://commerce.staqr.com/api
COMMERCE_USERNAME=your-keycloak-username
COMMERCE_PASSWORD=your-keycloak-password
COMMERCE_TENANT=PRVIDR
# Commerce (via proxy)
COMMERCE_PROXY_URL=https://my.staqr.com/api/commerce
Loading Environment Variables
Node.js (with dotenv):
import dotenv from 'dotenv';
dotenv.config();
// Variables now available via process.env
const apiKey = process.env.STAQR_API_KEY;
Python (with python-dotenv):
from dotenv import load_dotenv
load_dotenv()
# Variables now available via os.environ
api_key = os.environ.get("STAQR_API_KEY")
Troubleshooting
"401 Unauthorized"
Causes:
- Invalid or expired token
- Token for wrong environment (sandbox vs production)
- Missing required scopes
Solutions:
- Regenerate API key or refresh OAuth token
- Verify environment URLs match credentials
- Check key permissions in Settings
"403 Forbidden"
Causes:
- Token lacks required permissions
- Accessing resource from wrong tenant
- Resource doesn't exist or is deleted
Solutions:
- Check key scopes in Settings > API Keys
- Verify
X-Tenantorx-seller-idheader is correct - Verify resource exists
"Token Expired During Request"
Cause: Long-running requests with short-lived tokens.
Solution: Implement token refresh (see above) or use longer-lived tokens in Settings.
Next Steps
- TypeScript SDK - TypeScript-specific examples
- Python SDK - Python-specific examples
- Mobile Considerations - Security for mobile apps