> algolia-enterprise-rbac

Configure Algolia enterprise access control: team-scoped API keys, Secured API Keys for multi-tenant RBAC, dashboard team management, and audit logging. Trigger: "algolia RBAC", "algolia enterprise", "algolia roles", "algolia permissions", "algolia team access", "algolia multi-tenant", "algolia SSO".

fetch
$curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/algolia-enterprise-rbac?format=md"
SKILL.mdalgolia-enterprise-rbac

Algolia Enterprise RBAC

Overview

Algolia's access control is built on API keys with ACL (Access Control Lists). Each key has specific permissions, index restrictions, and rate limits. For multi-tenant apps, Secured API Keys provide per-user filtering without creating individual keys. For team management, Algolia's dashboard supports team members with role-based access.

API Key ACL Permissions

ACLOperations AllowedUse For
searchSearch queriesFrontend, search-only clients
browseBrowse/export all recordsData export, migration scripts
addObjectAdd or replace recordsIndexing pipelines
deleteObjectDelete recordsData cleanup, GDPR deletion
editSettingsModify index settingsDeployment scripts
listIndexesList all indicesMonitoring, health checks
deleteIndexDelete entire indicesAdmin operations only
analyticsRead analytics dataDashboards, reporting
recommendationAlgolia Recommend APIProduct recommendations
usageRead usage dataBilling monitoring
logsRead API logsDebugging, audit

Instructions

Step 1: Define Application Roles

import { algoliasearch } from 'algoliasearch';

const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);

// Role definitions with minimal permissions
const ROLES = {
  // Backend search service: search only, scoped to specific indices
  searchService: {
    acl: ['search'] as const,
    description: 'Search service — production read-only',
    indexes: ['products', 'articles'],
    maxQueriesPerIPPerHour: 100000,
  },

  // Indexing pipeline: write records, no search or delete
  indexingPipeline: {
    acl: ['addObject', 'editSettings', 'listIndexes'] as const,
    description: 'Indexing pipeline — write-only, no delete',
    indexes: ['products', 'articles'],
    maxQueriesPerIPPerHour: 10000,
  },

  // Analytics dashboard: read analytics, no data access
  analyticsDashboard: {
    acl: ['analytics', 'usage', 'listIndexes'] as const,
    description: 'Analytics reader — no record access',
    indexes: ['products', 'articles'],
    maxQueriesPerIPPerHour: 5000,
  },

  // Data admin: full CRUD, restricted to non-production
  dataAdmin: {
    acl: ['search', 'browse', 'addObject', 'deleteObject', 'editSettings', 'listIndexes', 'deleteIndex'] as const,
    description: 'Data admin — full access, staging only',
    indexes: ['staging_*'],
    maxQueriesPerIPPerHour: 50000,
  },
};

async function createRoleKey(roleName: keyof typeof ROLES) {
  const role = ROLES[roleName];
  const { key } = await client.addApiKey({
    apiKey: {
      acl: [...role.acl],
      description: role.description,
      indexes: role.indexes,
      maxQueriesPerIPPerHour: role.maxQueriesPerIPPerHour,
    },
  });
  console.log(`Created ${roleName} key: ...${key.slice(-8)}`);
  return key;
}

Step 2: Multi-Tenant RBAC with Secured API Keys

// Secured API Keys embed filters the client cannot bypass.
// Generate on YOUR server, send to the frontend.

interface UserContext {
  userId: string;
  tenantId: string;
  role: 'admin' | 'editor' | 'viewer';
}

function generateUserSearchKey(user: UserContext): string {
  // Base filter: tenant isolation
  let filters = `tenant_id:${user.tenantId}`;

  // Role-based visibility
  switch (user.role) {
    case 'admin':
      // Admins see everything in their tenant
      break;
    case 'editor':
      // Editors see published + their own drafts
      filters += ` AND (status:published OR author_id:${user.userId})`;
      break;
    case 'viewer':
      // Viewers see published only
      filters += ' AND status:published';
      break;
  }

  return client.generateSecuredApiKey({
    parentApiKey: process.env.ALGOLIA_SEARCH_KEY!,
    restrictions: {
      filters,
      validUntil: Math.floor(Date.now() / 1000) + 3600, // 1 hour
      restrictIndices: ['products', 'articles'],
    },
  });
}

// API endpoint: generate key for authenticated user
// GET /api/algolia/key
// Response: { appId: "...", searchKey: "secured_key_here" }

Step 3: Permission Checking Middleware

// Validate that the calling service has required Algolia permissions
async function validateKeyPermissions(
  apiKey: string,
  requiredAcl: string[]
): Promise<boolean> {
  try {
    const keyInfo = await client.getApiKey({ key: apiKey });
    const hasAll = requiredAcl.every(perm => keyInfo.acl.includes(perm));

    if (!hasAll) {
      const missing = requiredAcl.filter(p => !keyInfo.acl.includes(p));
      console.warn(`Key missing permissions: ${missing.join(', ')}`);
    }

    return hasAll;
  } catch (e) {
    console.error('Failed to validate API key:', e);
    return false;
  }
}

// Express middleware
function requireAlgoliaPermission(requiredAcl: string[]) {
  return async (req: any, res: any, next: any) => {
    const key = req.headers['x-algolia-api-key'];
    if (!key || !(await validateKeyPermissions(key, requiredAcl))) {
      return res.status(403).json({ error: 'Insufficient Algolia permissions' });
    }
    next();
  };
}

Step 4: API Key Audit and Rotation

// List all API keys and audit their permissions
async function auditApiKeys() {
  const { keys } = await client.listApiKeys();

  console.log(`Total API keys: ${keys.length}\n`);

  for (const key of keys) {
    const ageMs = Date.now() - new Date(key.createdAt * 1000).getTime();
    const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));

    console.log(`Key: ...${key.value.slice(-8)}`);
    console.log(`  Description: ${key.description || '(none)'}`);
    console.log(`  ACL: ${key.acl.join(', ')}`);
    console.log(`  Indices: ${key.indexes?.join(', ') || 'ALL'}`);
    console.log(`  Rate limit: ${key.maxQueriesPerIPPerHour || 'unlimited'}/hr`);
    console.log(`  Age: ${ageDays} days`);

    // Flag old keys
    if (ageDays > 90) {
      console.log(`  WARNING: Key is ${ageDays} days old — consider rotation`);
    }
    // Flag overly permissive keys
    if (key.acl.includes('deleteIndex') && !key.description?.includes('admin')) {
      console.log(`  WARNING: Has deleteIndex permission — verify this is intentional`);
    }
    console.log('');
  }
}

Step 5: Dashboard Team Management

Algolia Dashboard Team Roles (configured in dashboard.algolia.com > Team):

| Dashboard Role | Can Do                                    | Can't Do              |
|----------------|-------------------------------------------|-----------------------|
| Owner          | Everything + billing + team management    | N/A                   |
| Admin          | All index operations + API key management | Billing               |
| Editor         | Search, index data, edit settings         | API key management    |
| Viewer         | Search, view analytics                    | Modify anything       |

Configure at: dashboard.algolia.com > Settings > Team
Enterprise plans support SSO (SAML 2.0) for team authentication.

Security Checklist

  • Each microservice has its own scoped API key (not shared admin key)
  • Frontend keys are search-only with referers restriction
  • Multi-tenant apps use Secured API Keys with filters
  • maxQueriesPerIPPerHour set on all non-admin keys
  • Keys restricted to specific indexes (not all)
  • Key rotation scheduled (every 90 days)
  • Dashboard team members have appropriate roles
  • API key audit runs monthly

Error Handling

IssueCauseSolution
403 on searchKey missing search ACLCheck key permissions with getApiKey
Secured key invalidParent key deleted/rotatedRegenerate secured keys from new parent
Filter bypassClient-side filter manipulationSecured API Keys enforce filters server-side
Audit shows unknown keysLeaked or forgotten keysDelete unrecognized keys, rotate known ones

Resources

Next Steps

For major platform migrations, see algolia-migration-deep-dive.

┌ stats

installs/wk0
░░░░░░░░░░
github stars1.7K
██████████
first seenMar 23, 2026
└────────────

┌ repo

jeremylongshore/claude-code-plugins-plus-skills
by jeremylongshore
└────────────