Authentication Overview¶
This guide explains how authentication works in Centrali and how to choose the right method for your use case.
Authentication Methods¶
Centrali uses OAuth 2.0 for authentication. There are two primary ways to authenticate:
1. User Authentication (Dashboard Login)¶
Use for: - Accessing the Centrali web dashboard - Interactive administration tasks - Managing workspaces and settings
How it works: - Login at centrali.io - Session managed via browser cookies - Automatically handles token refresh
You don't need to implement this - it's handled by the Centrali dashboard.
2. Service Account Authentication (API & SDK) [Recommended]¶
Use for: - API integrations - SDK usage in applications - Server-to-server communication - CI/CD pipelines - Production deployments
How it works: 1. Create a service account in your workspace dashboard 2. Receive client_id and client_secret 3. Exchange credentials for a JWT access token 4. Use the token in API requests
This is the primary authentication method for building with Centrali.
Service Account Authentication Flow¶
Step 1: Create a Service Account¶
See the Account Setup Guide for detailed instructions.
You'll receive:
Step 2: Obtain an Access Token¶
Exchange your credentials for a JWT token:
Endpoint: POST https://auth.centrali.io/oidc/token
Request:
curl -X POST "https://auth.centrali.io/oidc/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=ci_a1b2c3d4e5f6g7h8i9j0" \
-d "client_secret=sk_0123456789abcdef..." \
-d "scope=openid"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 25200,
"scope": "openid"
}
Token Details: - Lifetime: 7 hours (25,200 seconds) - Type: JWT (JSON Web Token) - Refresh: Request a new token before expiration
Step 3: Use the Token in API Requests¶
Include the token in the Authorization header:
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/structures" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Using the SDK (Recommended)¶
The Centrali SDK handles authentication automatically:
import { CentraliSDK } from '@centrali-io/centrali-sdk';
const centrali = new CentraliSDK({
baseUrl: 'https://api.centrali.io',
workspaceId: 'my-workspace',
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET
});
// SDK automatically:
// 1. Fetches access token on first request
// 2. Caches the token
// 3. Refreshes before expiration
// 4. Retries on auth errors
// Just use the API:
const products = await centrali.queryRecords('Product', { limit: 10 });
Benefits: - ✅ Automatic token management - ✅ Automatic token refresh - ✅ Built-in retry logic - ✅ TypeScript support - ✅ Better error handling
See the SDK Guide for complete documentation.
Manual Token Management¶
If you're not using the SDK, you'll need to manage tokens yourself:
Best Practices¶
1. Cache Tokens Don't fetch a new token for every request:
class CentraliAuth {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.token = null;
this.tokenExpiry = null;
}
async getToken() {
// Return cached token if still valid
if (this.token && this.tokenExpiry && Date.now() < this.tokenExpiry) {
return this.token;
}
// Fetch new token
const response = await fetch('https://auth.centrali.io/oidc/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: this.clientId,
client_secret: this.clientSecret,
scope: 'openid'
})
});
const data = await response.json();
this.token = data.access_token;
// Refresh 5 minutes before actual expiration
this.tokenExpiry = Date.now() + ((data.expires_in - 300) * 1000);
return this.token;
}
}
// Usage
const auth = new CentraliAuth(clientId, clientSecret);
const token = await auth.getToken();
2. Handle Token Expiration
async function makeApiRequest(url, options = {}) {
const token = await auth.getToken();
const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`
}
});
// If unauthorized, token may have expired - retry once
if (response.status === 401) {
// Clear cached token and retry
auth.token = null;
const newToken = await auth.getToken();
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${newToken}`
}
});
}
return response;
}
Security Considerations¶
Storing Credentials¶
✅ Do: - Use environment variables - Use secret management services (AWS Secrets Manager, etc.) - Encrypt secrets at rest - Use different credentials per environment
❌ Don't: - Hardcode in source code - Commit to version control - Share via email or messaging - Log credentials (even accidentally)
Credential Rotation¶
Rotate service account credentials regularly:
Recommended Schedule: - Development: Every 90 days - Production: Every 30-90 days - Immediately: If credentials are compromised
How to Rotate: 1. Dashboard → Settings → Service Accounts 2. Select your service account 3. Click "Rotate Secret" 4. Update your application with the new client_secret 5. Old secret is immediately invalidated
Permission Scoping (Coming Soon)¶
In the future, you'll be able to: - Create service accounts with limited permissions - Restrict access to specific structures - Limit operations (read-only, write-only, etc.) - Set IP allowlists
Token Format & Claims¶
Service account JWT tokens include these claims:
{
"sub": "ci_a1b2c3d4e5f6g7h8i9j0", // Client ID
"iss": "https://auth.centrali.io", // Issuer
"aud": "https://centrali.io", // Audience
"iat": 1705326000, // Issued at (timestamp)
"exp": 1705351200, // Expires at (timestamp)
"workspace": "my-workspace", // Workspace slug
"isServiceAccount": "true", // Service account flag
"groups": ["developers"] // Assigned groups
}
You can decode and inspect tokens at jwt.io (never paste production tokens!).
Common Authentication Patterns¶
Pattern 1: Backend API Service¶
// server.js
import { CentraliSDK } from '@centrali-io/centrali-sdk';
const centrali = new CentraliSDK({
baseUrl: 'https://api.centrali.io',
workspaceId: process.env.CENTRALI_WORKSPACE,
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET
});
app.get('/api/products', async (req, res) => {
const products = await centrali.queryRecords('Product', {
filter: 'inStock = true',
limit: 20
});
res.json(products);
});
Pattern 2: CLI Tool¶
#!/usr/bin/env node
import { CentraliSDK } from '@centrali-io/centrali-sdk';
import dotenv from 'dotenv';
dotenv.config();
const centrali = new CentraliSDK({
baseUrl: 'https://api.centrali.io',
workspaceId: process.env.CENTRALI_WORKSPACE,
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET
});
async function exportData() {
const records = await centrali.queryRecords('Product', {});
console.log(JSON.stringify(records, null, 2));
}
exportData();
Pattern 3: Serverless Function (Vercel, AWS Lambda)¶
// api/products.js (Vercel)
import { CentraliSDK } from '@centrali-io/centrali-sdk';
// Initialize outside handler for connection reuse
const centrali = new CentraliSDK({
baseUrl: 'https://api.centrali.io',
workspaceId: process.env.CENTRALI_WORKSPACE,
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET
});
export default async function handler(req, res) {
const products = await centrali.queryRecords('Product', {
limit: 10
});
res.json(products);
}
Troubleshooting¶
"Invalid client credentials" Error¶
Causes: - Incorrect client_id or client_secret - Extra spaces or newlines in credentials - Service account was deleted or rotated
Solutions: - Verify credentials in dashboard - Check for whitespace in environment variables - Ensure you're using the correct workspace's credentials
"Token expired" Error¶
Causes: - Token lifetime exceeded (7 hours) - System clock skew
Solutions: - Fetch a new token - Implement automatic token refresh - Check system time is synchronized
"Forbidden" or "Unauthorized" Errors¶
Causes: - Token for wrong workspace - Missing Authorization header - Malformed token
Solutions: - Verify workspace slug matches token - Check Authorization: Bearer {token} header format - Ensure token hasn't been tampered with
See the Troubleshooting Guide for more help.
Related Documentation¶
- Account Setup - Create service accounts
- Service Account API - Complete API reference
- SDK Guide - Using the official SDK
- Troubleshooting - Common issues and solutions
Summary¶
For most use cases, use the Centrali SDK with service account credentials:
const centrali = new CentraliSDK({
baseUrl: 'https://api.centrali.io',
workspaceId: 'my-workspace',
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET
});
The SDK handles everything else - token fetching, caching, refresh, and retries.
Ready to build? Check out the Quick Start Guide!