Skip to content

Realtime Authentication

Learn how to authenticate realtime connections using bearer tokens or service account credentials.

Overview

Realtime connections require authentication to ensure only authorized users receive events. The SDK handles token management automatically, but understanding the flow helps with debugging and advanced use cases.

Authentication Methods

1. User Token (Browser Applications)

For browser applications where users are logged in:

import { CentraliSDK } from '@centrali-io/centrali-sdk';

const centrali = new CentraliSDK({
  baseUrl: 'https://api.centrali.io',
  workspaceId: 'my-workspace',
  token: userJwtToken  // From your auth flow
});

// Token is automatically used for realtime
centrali.realtime.subscribe({
  onEvent: handleEvent
});

Updating Tokens

If your token expires and you get a new one:

// Update token for subsequent requests
centrali.setToken(newToken);

// Realtime connections will use the new token on reconnect

2. Service Account (Server-Side Applications)

For server-to-server communication using client 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
});

// Token is automatically fetched on first use
centrali.realtime.subscribe({
  onEvent: handleEvent
});

The SDK automatically: - Fetches an access token using client credentials on first connection - Uses the token for the realtime connection

Important: The SDK caches the token and does NOT automatically refresh it when it expires. For long-running server applications, you should handle token expiry:

centrali.realtime.subscribe({
  onEvent: handleEvent,
  onError: async (error) => {
    if (error.code === 'TOKEN_EXPIRED') {
      // Manually refresh the token
      const newToken = await centrali.fetchServiceAccountToken();
      centrali.setToken(newToken);
      // SDK will reconnect with new token
    }
  }
});

Token Requirements

Workspace Scope

The token must be valid for the workspace you're connecting to. Tokens issued for workspace A cannot access realtime events for workspace B.

Required Permissions

The user or service account must have the realtime:subscribe permission for the workspace. This is typically included in standard user roles.

How Authentication Works

Connection Flow

  1. SDK requests a connection to /realtime/workspace/{ws}/events
  2. Token is passed as access_token query parameter (SSE limitation)
  3. Server validates the JWT against JWKS endpoint
  4. Server checks workspace claim matches the URL
  5. Server verifies IAM permissions via internal RPC
  6. Connection is established or error returned

Token Delivery

Since SSE connections cannot use custom headers in browsers, tokens are passed via query parameter:

GET /realtime/workspace/my-workspace/events?access_token=eyJhbG...

The SDK handles this automatically. For raw HTTP access, see the API Reference.

Error Handling

Authentication Errors

The SDK provides specific error codes for auth failures:

centrali.realtime.subscribe({
  onEvent: handleEvent,
  onError: (error) => {
    switch (error.code) {
      case 'MISSING_TOKEN':
        // No token provided - redirect to login
        redirectToLogin();
        break;

      case 'TOKEN_EXPIRED':
        // Token expired - refresh and retry
        refreshToken().then(() => reconnect());
        break;

      case 'INVALID_TOKEN':
        // Token is malformed or signature invalid
        console.error('Invalid token:', error.message);
        break;

      case 'WORKSPACE_MISMATCH':
        // Token is for a different workspace
        console.error('Token not valid for this workspace');
        break;

      case 'FORBIDDEN':
        // User doesn't have realtime permissions
        console.error('Permission denied');
        break;
    }
  }
});

Recoverable vs Non-Recoverable Errors

Error Code Recoverable Action
MISSING_TOKEN No Redirect to login
TOKEN_EXPIRED Yes Refresh token, SDK will retry
INVALID_TOKEN No Re-authenticate user
WORKSPACE_MISMATCH No Use correct workspace
FORBIDDEN No Request access
AUTH_ERROR Yes SDK will retry

Token Refresh Strategies

Browser Applications

Integrate with your auth provider's refresh mechanism:

import { useAuth } from 'your-auth-provider';

function MyComponent() {
  const { token, refreshToken } = useAuth();
  const centraliRef = useRef<CentraliSDK>();

  useEffect(() => {
    centraliRef.current = new CentraliSDK({
      baseUrl: 'https://api.centrali.io',
      workspaceId: 'my-workspace',
      token
    });

    const subscription = centraliRef.current.realtime.subscribe({
      onEvent: handleEvent,
      onError: async (error) => {
        if (error.code === 'TOKEN_EXPIRED') {
          const newToken = await refreshToken();
          centraliRef.current?.setToken(newToken);
          // Reconnection will use new token
        }
      }
    });

    return () => subscription.unsubscribe();
  }, []);

  // Update token when it changes
  useEffect(() => {
    centraliRef.current?.setToken(token);
  }, [token]);
}

Service Accounts

The SDK does not automatically refresh service account tokens. For long-running applications, handle token expiry in your error callback:

const centrali = new CentraliSDK({
  baseUrl: 'https://api.centrali.io',
  workspaceId: 'my-workspace',
  clientId: process.env.CENTRALI_CLIENT_ID,
  clientSecret: process.env.CENTRALI_CLIENT_SECRET
});

centrali.realtime.subscribe({
  onEvent: handleEvent,
  onError: async (error) => {
    if (error.code === 'TOKEN_EXPIRED') {
      // Fetch a new token and update the SDK
      const newToken = await centrali.fetchServiceAccountToken();
      centrali.setToken(newToken);
      // The SDK will automatically reconnect with the new token
    }
  }
});

Security Considerations

Token in URL

SSE requires passing the token in the URL query string. This means:

  1. Tokens may appear in server logs - Configure your load balancer to scrub access_token parameters
  2. Tokens visible in browser history - Not a concern for SSE connections (not navigated URLs)
  3. Use short-lived tokens - Reduces exposure window

Recommendations

  1. Use short token expiry (15-60 minutes) for browser tokens
  2. Rotate service account credentials periodically
  3. Monitor for unauthorized access using IAM audit logs
  4. Use HTTPS only - Never use realtime over HTTP

Testing Authentication

Verify Token Locally

// Decode JWT to check claims (don't use in production)
function decodeToken(token: string) {
  const payload = token.split('.')[1];
  return JSON.parse(atob(payload));
}

const claims = decodeToken(yourToken);
console.log('User ID:', claims.sub);
console.log('Workspace:', claims.workspaces);
console.log('Expires:', new Date(claims.exp * 1000));

Test Connection

const subscription = centrali.realtime.subscribe({
  onConnected: () => {
    console.log('Authentication successful!');
  },
  onError: (error) => {
    console.error('Auth failed:', error.code, error.message);
  },
  onEvent: () => {}  // Required
});