This guide shows how to use the Weaviate C# client's Role-Based Access Control (RBAC) APIs for managing database users, roles, permissions, assignments, and groups.
- Overview
- Version Requirements
- Client Initialization
- Users
- Roles
- Groups
- Permission Action Strings
- Error Handling
- Readiness & Test Infrastructure
- Best Practices
- Complete Example
The RBAC surface provides high-level async operations:
client.Users; // UsersClient - manages database users
client.Roles; // RolesClient - manages roles, permissions, and assignments
client.Groups; // GroupsClient - queries OIDC groupsUsersClient: Provides specialized sub-clients for database and OIDC user management:Users.Db: Create, list, get, delete database users; rotate API keys; activate/deactivate; manage role assignmentsUsers.Oidc: Manage role assignments for OIDC users
RolesClient: Create/list/get/delete roles, manage permissions, assign/revoke roles to users or groups, query assignmentsGroupsClient: Provides specialized sub-client for OIDC group management:Groups.Oidc: List OIDC groups and retrieve role assignments
| Feature | Minimum Version |
|---|---|
| RBAC APIs (users, roles, groups) | 1.28.0 |
| C# Client Integration Tests | 1.31.0 |
The C# client library targets Weaviate >= 1.31.0 for integration testing. Core RBAC features are available from Weaviate 1.28.0, but the client enforces 1.31.0 as the minimum supported version.
Use client.WeaviateVersion to check the server version at runtime if needed.
Example for an RBAC-enabled local deployment (REST 8092 / gRPC 50063):
var adminKey = Environment.GetEnvironmentVariable("WEAVIATE_ADMIN_API_KEY") ?? "admin-key";
var client = WeaviateClientBuilder
.Local(hostname: "localhost", restPort: 8092, grpcPort: 50063)
.WithCredentials(Auth.ApiKey(adminKey))
.Build();
if (!await client.IsReady()) throw new Exception("Weaviate not ready");The UsersClient provides specialized sub-clients for managing database and OIDC users:
client.Users.Db: Database user management (create, list, get, delete, activate, deactivate, rotate keys, assign/revoke roles)client.Users.Oidc: OIDC user role management (assign/revoke roles, get roles)
Get information about the currently authenticated user:
var me = await client.Users.OwnInfo();
Console.WriteLine($"Me: {me.Username}, Active={me.Active}, Roles={string.Join(",", me.Roles.Select(r => r.Name))}");List all database users:
var users = await client.Users.Db.List();
foreach (var u in users)
Console.WriteLine($"User: {u.UserId}, Active={u.Active}");Database users only (OIDC users are managed by the identity provider):
var newUserId = $"user-{Random.Shared.Next(1, 10_000)}";
var apiKey = await client.Users.Db.Create(newUserId);
var user = await client.Users.Db.Get(newUserId);
Console.WriteLine($"Created {user.UserId}, Active={user.Active}");await client.Users.Db.Delete(newUserId);Database users only:
var rotatedKey = await client.Users.Db.RotateApiKey(newUserId);
// Use rotatedKey to create a secondary client if neededDatabase users only:
await client.Users.Db.Deactivate(newUserId);
await client.Users.Db.Activate(newUserId);For database users:
await client.Users.Db.AssignRoles(newUserId, "viewer", "custom-role");
await client.Users.Db.RevokeRoles(newUserId, "viewer");For OIDC users:
await client.Users.Oidc.AssignRoles(oidcUserId, new[] { "viewer", "custom-role" });
await client.Users.Oidc.RevokeRoles(oidcUserId, new[] { "viewer" });For database users:
var rolesForUser = await client.Users.Db.GetRoles(newUserId);
foreach (var r in rolesForUser) Console.WriteLine(r.Name);For OIDC users:
var rolesForUser = await client.Users.Oidc.GetRoles(oidcUserId);
foreach (var r in rolesForUser) Console.WriteLine(r.Name);var hasViewer = (await client.Users.Db.GetRoles(newUserId)).Any(r => r.Name == "viewer");var roles = await client.Roles.ListAll();
foreach (var r in roles) Console.WriteLine(r.Name);var role = await client.Roles.Get("viewer");
Console.WriteLine($"Role: {role.Name}, Permissions: {role.Permissions.Count}");Creates a new role with optional initial permissions. Returns the created role details:
var roleName = $"role-{Guid.NewGuid():N}";
var createdRole = await client.Roles.Create(roleName, new[] { new Permissions.Roles { Read = true } });
Console.WriteLine($"Created role: {createdRole.Name}");Idempotent—returns success even if the role doesn't exist:
await client.Roles.Delete(roleName);await client.Roles.AddPermissions(roleName, new[] { new Permissions.Roles { Create = true } });
await client.Roles.RemovePermissions(roleName, new[] { new Permissions.Roles { Create = true } });Returns true if the role has the specified permission, false otherwise (including when the role doesn't exist):
var has = await client.Roles.HasPermission(roleName, new Permissions.Roles { Read = true });
Console.WriteLine($"Has permission: {has}");Note: This endpoint returns a boolean result with HTTP 200 status, even for non-existent roles. It does not throw exceptions for missing roles.
var assignments = await client.Roles.GetUserAssignments(roleName);
foreach (var a in assignments) Console.WriteLine(a.UserId);The GroupsClient provides specialized sub-clients for managing groups by type. Currently, only OIDC groups are supported.
Groups originate from identity providers (e.g., OIDC) and cannot be created via the client—they're synchronized from the external identity provider.
var groups = await client.Groups.Oidc.GetKnownGroupNames();
foreach (var g in groups)
Console.WriteLine($"Group: {g}");var groupId = "/example-group";
var groupRoles = await client.Groups.Oidc.GetRoles(groupId);
foreach (var r in groupRoles)
Console.WriteLine($"Role: {r.Name}");Permission actions are lowercase snake-case identifiers, but the C# client provides strongly-typed permission classes for each resource. You should construct permissions using these types, not raw strings.
Supported Permission Types:
| Type | Properties (actions) | Example Usage |
|---|---|---|
Permissions.Alias |
Create, Read, Update, Delete |
new Permissions.Alias { Read = true } |
Permissions.Data |
Create, Read, Update, Delete |
new Permissions.Data { Read = true } |
Permissions.Backups |
Manage |
new Permissions.Backups { Manage = true } |
Permissions.Cluster |
Read |
new Permissions.Cluster { Read = true } |
Permissions.Nodes |
Read |
new Permissions.Nodes { Read = true } |
Permissions.Roles |
Create, Read, Update, Delete |
new Permissions.Roles { Read = true } |
Permissions.Users |
Create, Read, Update, Delete, AssignAndRevoke |
new Permissions.Users { Create = true } |
Permissions.Tenants |
Create, Read, Update, Delete |
new Permissions.Tenants { Read = true } |
Permissions.Groups |
AssignAndRevoke, Read |
new Permissions.Groups { Read = true } |
Permissions.Replicate |
Create, Read, Update, Delete |
new Permissions.Replicate { Read = true } |
Permissions.Collections |
Create, Read, Update, Delete |
new Permissions.Collections { Read = true } |
Example:
// Grant read access to roles and collections
var permissions = new[] {
new Permissions.Roles { Read = true },
new Permissions.Collections { Read = true }
};
var createdRole = await client.Roles.Create(roleName, permissions);Wire Format Handling:
PermissionScope objects are converted directly to DTOs (Rest.Dto.Permission) by the client
RBAC operations follow standard Weaviate error handling patterns. For comprehensive information about exception types and error handling strategies, see the Error Handling Guide.
Idempotent Deletes:
await client.Roles.Delete("role-name");
await client.Roles.Delete("role-name");
// Succeeds even if role doesn't existLenient Permission Checks:
// Returns false instead of throwing for non-existent roles
var hasPermission = await client.Roles.HasPermission("unknown-role-name", new Permissions.Roles { Read = true });Conflict Handling:
try
{
await client.Roles.Create("existing-role", new[] { new Permissions.Roles { Read = true } });
}
catch (WeaviateConflictException ex)
{
Console.WriteLine($"Role already exists: {ex.Message}");
}Integration tests invoke _weaviate.IsReady() in a centralized InitializeAsync to fail fast if the server is down. Applications can apply the same readiness probe.
- Clean up transient roles/users with
try/finally. - Keep permissions minimal (least privilege).
- Rotate keys on credential lifecycle events per security policy.
- Gate RBAC-only flows behind version checks when supporting older deployments.
- Avoid caching permissions aggressively—revalidate on security-sensitive paths.
using Weaviate.Client;
using Weaviate.Client.Models;
var adminKey = Environment.GetEnvironmentVariable("WEAVIATE_ADMIN_API_KEY") ?? "admin-key";
var client = WeaviateClientBuilder.Local(restPort: 8092, grpcPort: 50063)
.WithCredentials(Auth.ApiKey(adminKey))
.Build();
if (!await client.IsReady()) throw new Exception("Weaviate not ready");
// Create a role with permissions
var roleName = $"demo-role-{Guid.NewGuid():N}";
var role = await client.Roles.Create(roleName, new[]
{
new Permissions.Roles { Read = true },
new Permissions.Collections { Read = true }
});
Console.WriteLine($"Created role: {role.Name}");
// Create a database user
var userId = $"demo-user-{Guid.NewGuid():N}";
var userKey = await client.Users.Db.Create(userId);
Console.WriteLine($"Created user {userId} with API key");
// Assign role to user
await client.Users.Db.AssignRoles(userId, roleName);
Console.WriteLine($"Assigned role {roleName} to user {userId}");
// Verify role assignment
var userRoles = await client.Users.Db.GetRoles(userId);
Console.WriteLine($"User roles: {string.Join(", ", userRoles.Select(r => r.Name))}");
// Check specific permission
var hasPermission = await client.Roles.HasPermission(roleName, new Permissions.Roles { Read = true });
Console.WriteLine($"Role has read_roles permission: {hasPermission}");
// List all role assignments for this user
var assignments = await client.Roles.GetUserAssignments(roleName);
Console.WriteLine($"Users with role {roleName}: {assignments.Count()}");
// Cleanup
await client.Users.Db.RevokeRoles(userId, roleName);
await client.Roles.Delete(roleName);
await client.Users.Db.Delete(userId);
Console.WriteLine("Cleanup complete");Generated automatically to accompany the RBAC client feature.