Skip to content

GridLab Keycloak .NET Client - Skills Guide

Skill Type: Identity & Access Management
Technology Stack: .NET, Keycloak, OAuth 2.0, OpenID Connect
Complexity Level: Intermediate
Last Updated: 2024


📋 Overview

The Keycloak .NET client provides a high-level C# interface for communicating with Keycloak identity and access management servers, enabling seamless integration of authentication and authorization in your applications.

Key Capabilities

  • ✅ Full Keycloak Admin REST API support
  • ✅ Realm and client management
  • ✅ User and group administration
  • ✅ Role-based access control (RBAC)
  • ✅ OpenID Connect integration
  • ✅ Multi-realm support

Prerequisites

  • .NET 9.0 or higher
  • Keycloak server (version 12.0 or higher recommended)
  • Admin credentials for Keycloak
  • NuGet package manager

🚀 Quick Start

Step 1: Install NuGet Package

Install the Keycloak.Net package from NuGet:

dotnet add package Keycloak.Net

Package Information:

Step 2: Configure Settings

Add Keycloak configuration to your appsettings.json:

{
  "Keycloak": {
    "Url": "https://your-keycloak-server/auth",
    "RealmName": "your-realm-name",
    "AdminUsername": "your-admin-username",
    "AdminPassword": "your-admin-password"
  }
}

Step 3: Create Configuration Class

Optionally, create a configuration class to hold settings:

public class KeycloakClientOptions
{
    public string Url { get; set; } = string.Empty;
    public string RealmName { get; set; } = string.Empty;
    public string AdminUserName { get; set; } = string.Empty;
    public string AdminPassword { get; set; } = string.Empty;
}

Step 4: Register Configuration

Configure options in your ABP module:

[DependsOn(
    //...other dependencies
    typeof(AbpAutofacModule) // Required for proper DI
)]
public class YourModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();

        Configure<KeycloakClientOptions>(options =>
        {
            options.Url = configuration["Keycloak:Url"]!;
            options.AdminUserName = configuration["Keycloak:AdminUsername"]!;
            options.AdminPassword = configuration["Keycloak:AdminPassword"]!;
            options.RealmName = configuration["Keycloak:RealmName"]!;
        });
    }
}

💡 Usage Examples

Client Management

public class KeycloakClientSeeder : ITransientDependency
{  
    private readonly KeycloakClient _keycloakClient;
    private readonly KeycloakClientOptions _keycloakOptions;
    private readonly ILogger<KeycloakClientSeeder> _logger;

    public KeycloakClientSeeder(
        IOptions<KeycloakClientOptions> keycloakClientOptions,
        ILogger<KeycloakClientSeeder> logger)
    {
        _keycloakOptions = keycloakClientOptions.Value;
        _logger = logger;

        _keycloakClient = new KeycloakClient(
            _keycloakOptions.Url,
            _keycloakOptions.AdminUserName,
            _keycloakOptions.AdminPassword
        );
    }

    public async Task CreateWebClientAsync()
    {
        var webClientId = "GMSS_Web";
        var webClient = (await _keycloakClient.GetClientsAsync(
            _keycloakOptions.RealmName,
            clientId: webClientId
        )).FirstOrDefault();

        if (webClient == null)
        {
            var webRootUrl = "https://localhost:44322";

            webClient = new Client
            {
                ClientId = webClientId,
                Name = "Web Client",
                Protocol = "openid-connect",
                Enabled = true,
                BaseUrl = webRootUrl,
                RedirectUris = new List<string>
                {
                    $"{webRootUrl}/signin-oidc"
                },
                ImplicitFlowEnabled = true,  // for hybrid flow
                FrontChannelLogout = true,
                PublicClient = false,
                Secret = "1q2w3e*"
            };

            webClient.Attributes = new Dictionary<string, object>
            {
                { "post.logout.redirect.uris", $"{webRootUrl}/signout-callback-oidc" }
            };

            await _keycloakClient.CreateClientAsync(_keycloakOptions.RealmName, webClient);

            _logger.LogInformation("Web client {ClientId} created successfully", webClientId);
        }
        else
        {
            _logger.LogInformation("Web client {ClientId} already exists", webClientId);
        }
    }
}

User Management

public class KeycloakUserService : ITransientDependency
{
    private readonly KeycloakClient _keycloakClient;
    private readonly string _realmName;

    public KeycloakUserService(IOptions<KeycloakClientOptions> options)
    {
        var opts = options.Value;
        _keycloakClient = new KeycloakClient(opts.Url, opts.AdminUserName, opts.AdminPassword);
        _realmName = opts.RealmName;
    }

    public async Task<User> CreateUserAsync(string username, string email, string firstName, string lastName)
    {
        var user = new User
        {
            UserName = username,
            Email = email,
            FirstName = firstName,
            LastName = lastName,
            Enabled = true,
            EmailVerified = true
        };

        await _keycloakClient.CreateUserAsync(_realmName, user);

        return user;
    }

    public async Task AssignRoleToUserAsync(string userId, string roleName)
    {
        var role = await _keycloakClient.GetRoleByNameAsync(_realmName, roleName);

        if (role != null)
        {
            await _keycloakClient.AddRealmRoleMappingsToUserAsync(
                _realmName,
                userId,
                new[] { role }
            );
        }
    }
}

Realm Management

public class KeycloakRealmService : ITransientDependency
{
    private readonly KeycloakClient _keycloakClient;

    public KeycloakRealmService(IOptions<KeycloakClientOptions> options)
    {
        var opts = options.Value;
        _keycloakClient = new KeycloakClient(opts.Url, opts.AdminUserName, opts.AdminPassword);
    }

    public async Task<Realm> CreateRealmAsync(string realmName, string displayName)
    {
        var realm = new Realm
        {
            RealmName = realmName,
            DisplayName = displayName,
            Enabled = true,
            SslRequired = "external",
            RegistrationAllowed = true,
            LoginWithEmailAllowed = true,
            DuplicateEmailsAllowed = false,
            ResetPasswordAllowed = true,
            EditUsernameAllowed = false,
            BruteForceProtected = true
        };

        await _keycloakClient.CreateRealmAsync(realm);

        return realm;
    }
}

📚 Best Practices

✅ Do's

  • Store admin credentials securely (use Azure Key Vault, AWS Secrets Manager, etc.)
  • Use service accounts for automated operations
  • Implement proper error handling
  • Use HTTPS for all Keycloak communications
  • Validate realm and client existence before operations
  • Log all administrative actions

❌ Don'ts

  • Don't hardcode credentials in source code
  • Don't use admin account for regular operations
  • Don't expose Keycloak admin endpoints publicly
  • Don't skip SSL certificate validation in production
  • Don't store tokens in insecure locations

🔍 Troubleshooting

Common Issues

Issue: Authentication failed

  • Solution: Verify admin username and password
  • Solution: Check if admin user has required permissions
  • Solution: Ensure realm name is correct

Issue: Connection timeout

  • Solution: Verify Keycloak server URL
  • Solution: Check network connectivity
  • Solution: Verify firewall settings

Issue: Client already exists error

  • Solution: Check for existing clients before creation
  • Solution: Use update operation instead of create

📖 Additional Resources

This documentation should help you set up and use the Keycloak client in your C# project.