Authentication¶
The Centrali SDK supports three authentication methods. Choose the right one based on your use case.
Workspace slug terminology
The SDK still uses the option name workspaceId, but the value you pass is your workspace slug such as acme-corp, not an internal UUID.
Which method should I use?¶
| Scenario | Method | SDK Option |
|---|---|---|
| Frontend app (React, Vue, public) | Publishable key | publishableKey |
| Frontend with user login (Clerk, Auth0) | External token | getToken |
| Third-party app acting as a user | OAuth (auth code + PKCE) | getToken |
| Third-party app with scoped machine access | OAuth (client credentials) | getToken |
| Server-side script or backend (policy-based auth) | Service account | clientId + clientSecret |
Publishable Keys¶
Publishable keys are scoped, browser-safe credentials for frontend apps. They bypass workspace policies — only the key's scopes determine what it can access.
Create a publishable key in the console, select which collections, triggers, and files it can access, then use it in your app:
import { CentraliSDK } from '@centrali-io/centrali-sdk';
const centrali = new CentraliSDK({
baseUrl: 'https://centrali.io',
workspaceId: 'my-workspace', // current SDK option name; pass the workspace slug
publishableKey: 'pk_live_a1b2c3d4e5f6g7h8',
});
// Read records (if scoped)
const posts = await centrali.queryRecords('posts');
// Submit a form (if scoped)
await centrali.createRecord('contact-submissions', {
name: 'Jane',
message: 'Hello!',
});
Key characteristics:
- Sent as
x-api-keyheader (not Bearer token) - No token refresh needed — the key is static
- Scopes follow
resource:action:targetformat (e.g.,records:list:posts) - Wildcard targets (
*) only allowed for read actions - Admin resources (users, groups, policies) are never accessible
- Rate limited per key: 200 reads/min, 30 writes/min
Security: Publishable keys are safe to expose in client-side code. They are workspace-scoped and can only access the specific resources their scopes allow.
External Tokens (BYOT)¶
If your app has its own authentication (Clerk, Auth0, Okta, etc.), use the getToken callback to pass your provider's JWT to Centrali:
const centrali = new CentraliSDK({
baseUrl: 'https://centrali.io',
workspaceId: 'my-workspace', // current SDK option name; pass the workspace slug
getToken: async () => {
// Get a fresh token from your auth provider
return await clerk.session.getToken();
},
});
// Full access governed by Centrali policies
await centrali.updateRecord('posts', 'post-123', { title: 'Updated' });
Key characteristics:
- Token is sent as
Authorization: Bearerheader getTokenis called before each request to ensure a fresh token- On 401, the SDK retries once with a new token from
getToken - Access is governed by Centrali policies and group membership (workspace policies, roles, groups apply)
- Requires an external auth provider configured in the console
You can also pass a static token directly:
const centrali = new CentraliSDK({
baseUrl: 'https://centrali.io',
workspaceId: 'my-workspace', // current SDK option name; pass the workspace slug
token: 'eyJhbGciOiJSUzI1NiIs...',
});
OAuth Apps¶
OAuth apps let third-party applications access Centrali on behalf of a user or as a machine identity. Create an OAuth app in Console > Settings > OAuth Apps, select scopes, and choose a grant type.
OAuth Scopes (Client Credentials)¶
For server-to-server integrations where no user is involved. The app authenticates with its own credentials and receives scoped access:
const centrali = new CentraliSDK({
baseUrl: 'https://centrali.io',
workspaceId: 'my-workspace', // current SDK option name; pass the workspace slug
getToken: async () => {
// Exchange client credentials for an access token
const resp = await fetch('https://auth.centrali.io/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.OAUTH_CLIENT_ID,
client_secret: process.env.OAUTH_CLIENT_SECRET,
}),
});
const data = await resp.json();
return data.access_token;
},
});
OAuth Authorization Code + PKCE (User Login)¶
For browser apps, CLI tools, or AI agents that need to act as a specific user. The user logs in via a browser and approves access:
- Redirect the user to the authorization endpoint
- User logs in and approves the requested scopes
- Your app receives an authorization code at the redirect URI
- Exchange the code (+ PKCE verifier) for access and refresh tokens
GET https://auth.centrali.io/oauth/authorize?
response_type=code&
client_id=oc_your_client_id&
redirect_uri=http://localhost:3000/callback&
scope=records:read records:list&
state=random-csrf-state&
code_challenge=BASE64URL(SHA256(code_verifier))&
code_challenge_method=S256
After the user approves, exchange the code:
POST https://auth.centrali.io/oauth/token
grant_type=authorization_code
code=AUTHORIZATION_CODE
redirect_uri=http://localhost:3000/callback
client_id=oc_your_client_id
code_verifier=YOUR_CODE_VERIFIER
Response:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "ort_...",
"scope": "records:read records:list"
}
Key characteristics:
- Tokens carry the user's identity (
sub= user ID, not client ID) - Access governed by OAuth scopes — only approved permissions are granted
- Refresh tokens rotate on each use (old token is invalidated)
- Consent is remembered — returning users skip the approval screen
- PKCE is required for public clients, optional for confidential clients
- Only S256 challenge method is supported
Available Scopes¶
| Scope | Description |
|---|---|
records:read | Read records |
records:write | Create, update, delete records |
records:list | List records |
structures:read | Read collection definitions |
structures:list | List collections |
queries:read | Read smart queries |
queries:write | Create, update, delete queries |
queries:list | List smart queries |
queries:execute | Execute smart queries |
files:read | Read and download files |
files:write | Upload, update, delete files |
files:list | List files |
folders:read | Read folder details |
folders:write | Create, update, delete folders |
folders:list | List folders |
compute:read | Read function definitions |
compute:write | Create, update, delete functions |
compute:list | List functions |
compute:execute | Execute functions |
orchestrations:read | Read orchestrations |
orchestrations:write | Create, update, delete orchestrations |
orchestrations:list | List orchestrations |
orchestrations:execute | Trigger orchestration runs |
search:read | Search entries |
pages:read | Read pages |
pages:write | Create, update, delete pages |
pages:list | List pages |
workspace:read | Read workspace information |
Client Types¶
| Type | Secret | Use Case |
|---|---|---|
| Confidential | Has a client secret | Server-side apps, backend services |
| Public | No secret (PKCE required) | Browser apps, CLIs, native apps, MCP |
Service Accounts¶
Service accounts use OIDC client credentials for server-to-server authentication with Centrali policy-based permissions (roles, groups, policies). Never use client secrets in browser code.
const centrali = new CentraliSDK({
baseUrl: 'https://centrali.io',
workspaceId: 'my-workspace', // current SDK option name; pass the workspace slug
clientId: process.env.CENTRALI_CLIENT_ID,
clientSecret: process.env.CENTRALI_CLIENT_SECRET,
});
// Full access governed by Centrali policies
await centrali.deleteRecord('posts', 'post-123');
Key characteristics:
- Token is fetched automatically via client credentials grant on first request
- On 401/403, the SDK refreshes the token and retries once
- Access is governed by Centrali policies and group membership
- Create service accounts in the console, then follow the service account guide
Mutual Exclusion¶
Only one auth method can be used at a time. The SDK throws an error if conflicting options are provided:
// This throws: "Cannot use publishableKey with clientId/clientSecret"
new CentraliSDK({
publishableKey: 'pk_live_...',
clientId: 'ci_...',
clientSecret: 'sk_...',
});
Publishable Keys vs Centrali Policies¶
Publishable keys and Centrali policies are separate authorization systems:
| Publishable Keys | Centrali Policies | |
|---|---|---|
| Used by | publishableKey auth path | token, getToken, clientId/clientSecret |
| Authorization | Scopes embedded in the key | Policies evaluated by IAM |
| Managed in | Console | Console |
| Granularity | resource:action:target | Conditions (time, IP, claims, groups) |
| Admin access | Never | Policy-dependent |
If you need conditional access (time-based, IP-restricted, group-based), use Centrali policies with user tokens or service accounts. If you need simple, scoped frontend access, use publishable keys.