KeyCloak.ManagementApi
1.4.2
dotnet add package KeyCloak.ManagementApi --version 1.4.2
NuGet\Install-Package KeyCloak.ManagementApi -Version 1.4.2
<PackageReference Include="KeyCloak.ManagementApi" Version="1.4.2" />
<PackageVersion Include="KeyCloak.ManagementApi" Version="1.4.2" />
<PackageReference Include="KeyCloak.ManagementApi" />
paket add KeyCloak.ManagementApi --version 1.4.2
#r "nuget: KeyCloak.ManagementApi, 1.4.2"
#:package KeyCloak.ManagementApi@1.4.2
#addin nuget:?package=KeyCloak.ManagementApi&version=1.4.2
#tool nuget:?package=KeyCloak.ManagementApi&version=1.4.2
KeyCloak.ManagementApi
A .NET Standard 2.0 library for interacting with KeyCloak's Management API, designed with a similar API structure to Auth0.ManagementApi for easier migration from Auth0 to KeyCloak.
Compatibility
- .NET Standard 2.0 (compatible with .NET Framework 4.6.1+ and .NET Core 2.0+)
- .NET Framework 4.6.1+
- .NET Core 2.0+
- .NET 5.0+
- .NET 6.0+
- .NET 8.0+
Installation
You can reference this project directly or package it as a NuGet package.
To create a NuGet package:
dotnet pack -c Release
Getting Started
Authentication
First, you need to obtain an access token from KeyCloak. This can be done using client credentials flow:
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Threading.Tasks;
public async Task<string> GetAccessTokenAsync(
string keycloakUrl,
string realm,
string clientId,
string clientSecret)
{
using (var httpClient = new HttpClient())
{
var tokenEndpoint = $"{keycloakUrl}/realms/{realm}/protocol/openid-connect/token";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret)
});
var response = await httpClient.PostAsync(tokenEndpoint, content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
return result.access_token;
}
}
Initialize the Management API Client
using KeyCloak.ManagementApi;
var keycloakUrl = "https://keycloak.example.com";
var realm = "master";
var accessToken = await GetAccessTokenAsync(keycloakUrl, realm, clientId, clientSecret);
var client = new ManagementApiClient(keycloakUrl, realm, accessToken);
Usage Examples
User Management
Get all users
var users = await client.Users.GetAllAsync();
foreach (var user in users)
{
Console.WriteLine($"{user.Username} - {user.Email}");
}
Get a specific user
var user = await client.Users.GetAsync("user-id-here");
Console.WriteLine($"User: {user.Username} ({user.Email})");
Create a new user
using KeyCloak.ManagementApi.Models;
var newUser = new UserCreateRequest
{
Username = "john.doe",
Email = "john.doe@example.com",
FirstName = "John",
LastName = "Doe",
Enabled = true,
EmailVerified = true,
Credentials = new List<Credential>
{
new Credential
{
Type = "password",
Value = "SecurePassword123!",
Temporary = false
}
}
};
var userId = await client.Users.CreateAsync(newUser);
Console.WriteLine($"Created user with ID: {userId}");
Update a user
var updateRequest = new UserUpdateRequest
{
Email = "newemail@example.com",
FirstName = "John",
LastName = "Doe",
Enabled = true
};
await client.Users.UpdateAsync("user-id-here", updateRequest);
Delete a user
await client.Users.DeleteAsync("user-id-here");
Reset user password
await client.Users.ResetPasswordAsync("user-id-here", "NewPassword123!", temporary: false);
Role Management
Get all roles
var roles = await client.Roles.GetAllAsync();
foreach (var role in roles)
{
Console.WriteLine($"Role: {role.Name} - {role.Description}");
}
Create a new role
var newRole = new RoleCreateRequest
{
Name = "premium-user",
Description = "Premium user role with additional privileges"
};
await client.Roles.CreateAsync(newRole);
Assign roles to a user
// Get the roles you want to assign
var roles = await client.Roles.GetAllAsync();
var premiumRole = roles.Find(r => r.Name == "premium-user");
await client.Users.AssignRolesAsync("user-id-here", new List<Role> { premiumRole });
Get user's roles
var userRoles = await client.Users.GetRolesAsync("user-id-here");
foreach (var role in userRoles)
{
Console.WriteLine($"User has role: {role.Name}");
}
Client (Application) Management
Get all clients
var clients = await client.Clients.GetAllAsync();
foreach (var c in clients)
{
Console.WriteLine($"Client: {c.ClientId} - {c.Name}");
}
Create a new client
var newClient = new ClientCreateRequest
{
ClientId = "my-app",
Name = "My Application",
Enabled = true,
PublicClient = false,
StandardFlowEnabled = true,
DirectAccessGrantsEnabled = true,
ServiceAccountsEnabled = true,
RedirectUris = new List<string>
{
"https://myapp.example.com/callback"
},
WebOrigins = new List<string>
{
"https://myapp.example.com"
}
};
var clientId = await client.Clients.CreateAsync(newClient);
Console.WriteLine($"Created client with ID: {clientId}");
Get client secret
var secret = await client.Clients.GetSecretAsync("client-id-here");
Console.WriteLine($"Client secret: {secret}");
Regenerate client secret
var newSecret = await client.Clients.RegenerateSecretAsync("client-id-here");
Console.WriteLine($"New client secret: {newSecret}");
Group Management
Get all groups
var groups = await client.Groups.GetAllAsync();
foreach (var group in groups)
{
Console.WriteLine($"Group: {group.Name} ({group.Path})");
}
Create a new group
var newGroup = new GroupCreateRequest
{
Name = "administrators"
};
var groupId = await client.Groups.CreateAsync(newGroup);
Add user to a group
await client.Users.JoinGroupAsync("user-id-here", "group-id-here");
Get group members
var members = await client.Groups.GetMembersAsync("group-id-here");
foreach (var member in members)
{
Console.WriteLine($"Member: {member.Username}");
}
Create a subgroup
var subGroup = new GroupCreateRequest
{
Name = "sub-admins"
};
var subGroupId = await client.Groups.CreateSubGroupAsync("parent-group-id", subGroup);
Migration from Auth0.ManagementApi
The API structure is intentionally similar to Auth0.ManagementApi. Here's a comparison:
Auth0 Code:
// Auth0
var auth0Client = new ManagementApiClient(token, new Uri(domain));
var users = await auth0Client.Users.GetAllAsync(new GetUsersRequest());
var user = await auth0Client.Users.GetAsync("user-id");
KeyCloak Equivalent:
// KeyCloak
var keycloakClient = new ManagementApiClient(baseUrl, realm, token);
var users = await keycloakClient.Users.GetAllAsync();
var user = await keycloakClient.Users.GetAsync("user-id");
Key Differences from Auth0
- Realm-based: KeyCloak is multi-tenant with realms, so you specify the realm when initializing
- Authentication: You need to obtain the access token separately (typically using client credentials flow)
- User Attributes: In KeyCloak, custom attributes are stored in a dictionary:
Dictionary<string, List<string>> - Client vs Application: What Auth0 calls "Applications" or "Clients", KeyCloak also calls "Clients"
Error Handling
The library throws exceptions on API errors. Always wrap calls in try-catch blocks:
try
{
var user = await client.Users.GetAsync("user-id");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"API Error: {ex.Message}");
}
Custom HttpClient
If you need to configure the HttpClient (for proxies, custom timeouts, etc.):
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
var client = new ManagementApiClient(keycloakUrl, realm, accessToken, httpClient);
License
This project is provided as-is for migrating from Auth0 to KeyCloak.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.4)
- System.Net.Http (>= 4.3.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 1.4.2:
- Fixed BulkGrantRolesToUserAsync and BulkRevokeRolesFromUserAsync endpoint paths (was missing /users/ segment)
Version 1.4.1:
- Fixed GetAllAsync to paginate through all organizations (Phase Two API defaults to small page size)
- Added GetPageAsync for explicit pagination control
Version 1.4.0:
- Added SearchByAttributeAsync for searching organizations by attribute query (q parameter)
Version 1.3.0:
- Added Phase Two Organizations support (IOrganizationsClient)
- Organization CRUD (GetAll, Get, Create, Update, Delete)
- Organization members (Add, Remove, GetMembers, SetMemberAttributes)
- Organization roles (GetRoles, CreateRole, DeleteRole)
- Role-user assignment (AssignRoleToUser, RemoveRoleFromUser)
- Composite org role extension (LinkClientRolesToOrgRole)
- Added realm-level API methods to ApiConnection for Phase Two endpoints
Version 1.2.1:
- Fixed GetByEmailAsync to use exact match instead of partial/LIKE search
- Fixed GetByClientIdAsync to properly URL-encode the clientId parameter
Version 1.2.0:
- Added FederatedIdentities support in User model
- Added GetUsersRequest with advanced filtering (username, email, firstName, lastName, enabled, emailVerified, search)
- Added attribute-based query filtering using the Query parameter
- Added PaginationInfo for improved pagination support
Version 1.1.0:
- Added client role management for users (GetClientRolesAsync, AssignClientRolesAsync, RemoveClientRolesAsync)
- Added client role management (GetRolesAsync, CreateRoleAsync, DeleteRoleAsync)
Version 1.0.0:
- Initial release
- User management (CRUD operations, password reset, role assignments)
- Role management (realm and client roles)
- Client management (CRUD operations, secret management)
- Group management (CRUD operations, subgroups, memberships)
- Compatible with .NET Framework 4.6.1+ and .NET Core 2.0+