Olympus Docs
SecurityAuthorization

Authorization, RBAC

Role-Based Access Control with Olympus

Role-Based Access Control: users have roles, roles grant permissions. Simple, well-understood, sufficient for most apps.

Olympus's RBAC primitive

The identity schema declares a role trait:

"role": {
  "type": "string",
  "enum": ["admin", "operator", "user"],
  "default": "user"
}

Hydra includes role in the ID token (as a custom claim). Your app reads it and enforces.

Defining permissions

In your app code (not in Olympus):

const PERMISSIONS = {
  admin: ["read", "write", "delete", "manage_users", "view_audit_log"],
  operator: ["read", "write", "view_audit_log"],
  user: ["read"],
};

function can(role: string, action: string): boolean {
  return PERMISSIONS[role]?.includes(action) ?? false;
}

Per-route enforcement

async function handler(req: Request) {
  const session = await getSession(req);
  if (!can(session.role, "delete")) {
    return Response.json({ error: "forbidden" }, { status: 403 });
  }
  // proceed with deletion
}

When RBAC isn't enough

  • Resource-specific permissions: "Alice can edit document X but not document Y." → ABAC or ReBAC.
  • Dynamic permissions: "Alice can edit document X only during business hours." → ABAC.
  • Hierarchical: "Manager of team X has all rights over team X members." → ReBAC.

Storing roles

In Kratos identity traits. Athena lets admins assign roles via the identity edit UI.

Alternative: store in your app DB and join on identity_id. Useful for many-roles-per-user or per-resource roles.

Role hierarchy

For nested roles ("admin includes everything operator can"):

const ROLE_HIERARCHY: Record<string, string[]> = {
  admin: ["operator", "user"],
  operator: ["user"],
  user: [],
};

function expandRoles(role: string): string[] {
  return [role, ...(ROLE_HIERARCHY[role] || []).flatMap(expandRoles)];
}

function can(role: string, action: string): boolean {
  return expandRoles(role).some(r => PERMISSIONS[r]?.includes(action));
}

Multi-role

If users can have multiple roles:

"roles": {
  "type": "array",
  "items": { "type": "string" }
}

Check roles.some(r => can(r, action)).

On this page