Skip to main content

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:

APIAuth MethodToken TypeTypical Use
Staqr PlatformJWTBearer TokenPlatform operations
Commerce DirectKeycloak OAuth2Access TokenBilling/CRM operations
Commerce via Staqr ProxyStaqr JWTBearer TokenSimplified Commerce access

Staqr Platform API

Obtaining API Keys

  1. Log in to your Staqr instance
  2. Navigate to Settings > API Keys
  3. Click Create New Key
  4. Copy the key (shown only once)
  5. 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

Bad Practice
// NEVER DO THIS
const config = new Configuration({
accessToken: 'YOUR_API_KEY_HERE' // Exposed in code!
});
Good Practice
// 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

EnvironmentStorage Method
Local Dev.env file (gitignored)
CI/CDPipeline secrets
ProductionSecret manager (AWS/GCP/Azure)
MobileBackend 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:

  1. Invalid or expired token
  2. Token for wrong environment (sandbox vs production)
  3. Missing required scopes

Solutions:

  1. Regenerate API key or refresh OAuth token
  2. Verify environment URLs match credentials
  3. Check key permissions in Settings

"403 Forbidden"

Causes:

  1. Token lacks required permissions
  2. Accessing resource from wrong tenant
  3. Resource doesn't exist or is deleted

Solutions:

  1. Check key scopes in Settings > API Keys
  2. Verify X-Tenant or x-seller-id header is correct
  3. 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