Skip to content

Seeding SPI (keycloak-seeding)

The keycloak-seeding extension automates Keycloak realm provisioning at startup. It reads JSON configuration files and idempotently creates realms, clients, roles, and groups — eliminating manual Keycloak setup steps from environment provisioning workflows.


Purpose

Without the seeding SPI, every new environment (dev, stage, preprod, uat, prod) would require a DevOps engineer to:

  1. Open the Keycloak admin console
  2. Manually create the microtec and businessowner realms
  3. Create 10+ OIDC clients (one per MFE app)
  4. Configure client scopes, redirect URIs, token mappers
  5. Create roles and default groups

With the seeding SPI, this entire process runs automatically when the Keycloak container starts.


Idempotency

The seeding SPI is fully idempotent. It can be run any number of times (container restarts, rolling updates, re-deployments) without creating duplicate resources.

Safe to Restart

Restarting the Keycloak container will NOT create duplicate realms, clients, or roles. The SPI checks for existence before creating any resource.

Idempotency checks by resource type:

ResourceCheck MethodBehavior if Exists
RealmRealmManager.getRealmByName()Skip entirely
Clientrealm.getClientByClientId()Skip
Rolerealm.getRole(roleName)Skip
Grouprealm.getGroupByPath()Skip
UseruserProvider.getUserByUsername()Skip

Configuration File Structure

Seed files are mounted into the container at /opt/keycloak/seed/:

/opt/keycloak/seed/
├── realm-microtec.json
├── realm-businessowner.json
└── default-users.json          (optional, dev/stage only)

Realm Config Schema

json
{
  "realm": "microtec",
  "displayName": "Microtec ERP",
  "enabled": true,
  "ssoSessionMaxLifespan": 36000,
  "accessTokenLifespan": 1800,
  "refreshTokenMaxReuse": 5,
  "loginTheme": "microtec-rtl",
  "accountTheme": "microtec-rtl",
  "internationalizationEnabled": true,
  "supportedLocales": ["ar", "en"],
  "defaultLocale": "ar",
  "clients": [
    {
      "clientId": "apps-accounting",
      "protocol": "openid-connect",
      "publicClient": true,
      "enabled": true,
      "redirectUris": [
        "https://dev.microtec-test.com/accounting/*",
        "http://localhost:4402/*"
      ],
      "webOrigins": ["*"],
      "standardFlowEnabled": true,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": false
    }
  ],
  "roles": {
    "realm": [
      { "name": "erp-user", "description": "Standard ERP user" },
      { "name": "erp-admin", "description": "ERP administrator" },
      { "name": "tenant-owner", "description": "Company tenant owner" }
    ]
  },
  "groups": [
    {
      "name": "ERP Users",
      "path": "/ERP Users",
      "realmRoles": ["erp-user"]
    }
  ]
}

ConfigureRealmClients Step

After realm creation, the SPI runs a dedicated ConfigureRealmClients step that applies post-creation client configuration:

Why account-console is Disabled

The account-console client provides Keycloak's built-in self-service account management UI. For Microtec ERP:

  • ERP users manage their profiles through the custom ERP application, not Keycloak's UI
  • Disabling reduces the attack surface
  • The account client (REST API only) remains enabled for programmatic access
java
// SetClientEnabledAsync method added to SPI
public void setClientEnabledAsync(RealmModel realm, String clientId, boolean enabled) {
    ClientModel client = realm.getClientByClientId(clientId);
    if (client != null) {
        client.setEnabled(enabled);
        log.infof("Client '%s' in realm '%s' set to enabled=%b", clientId, realm.getName(), enabled);
    } else {
        log.warnf("Client '%s' not found in realm '%s'", clientId, realm.getName());
    }
}

Usage in ConfigureRealmClients:

java
// Disable account-console (self-service UI not needed)
setClientEnabledAsync(realm, "account-console", false);

// Enable account (REST API used by ERP apps)
setClientEnabledAsync(realm, "account", true);

Protocol Mappers Applied

The seeding SPI automatically configures protocol mappers for JWT token enrichment:

Mapper NameTypeClaimDescription
tenant-idUser AttributetenantIdInjects tenant database context
tenant-nameUser AttributetenantNameHuman-readable company name
user-rolesRolerolesRealm roles in token
phone-numberUser AttributephoneNumberFor phone-based features
localeUser Attributelocalear/en for UI localization

Environment-Specific Seeding

Different environments receive different seed configurations:

Environmentdefault-users.jsonaccountThemeMin Clients
devYes (test users)microtec-devAll 10 MFE clients
stageYes (QA users)microtec-rtlAll 10 MFE clients
preprodNomicrotec-rtlAll 10 MFE clients
uatNomicrotec-rtlAll 10 MFE clients
productionNomicrotec-rtlAll 10 MFE clients

Production Seeding

Default users are NEVER seeded in production. The default-users.json file is only mounted in dev and stage environments. Production Keycloak instances are provisioned with realms and clients only.


Container Configuration

The seed files are passed to the container via ConfigMap mount (on-prem) or Azure Blob → Init Container (Azure):

yaml
# Azure Container App environment variable
- name: KEYCLOAK_SEED_ENABLED
  value: "true"
- name: KEYCLOAK_SEED_PATH
  value: "/opt/keycloak/seed"

# Volume mount (via init container copying from blob)
volumeMounts:
  - volumeName: seed-config
    mountPath: /opt/keycloak/seed
    readOnly: true

Troubleshooting

SymptomLikely CauseResolution
Realm not created on startupKEYCLOAK_SEED_ENABLED=falseSet to true in CAE env vars
Duplicate client errorIdempotency check failedCheck Keycloak DB for partial state; verify SPI version
account-console still enabledConfigureRealmClients step skippedCheck SPI logs for step execution; verify JAR version
Seed file not foundVolume mount failedCheck init container logs; verify blob access
JSON parse errorInvalid realm configValidate JSON against schema; check special characters
bash
# Check seeding logs in Keycloak container
kubectl logs -l app=keycloak --since=5m | grep -i "seeding\|seed\|realm"

# Or via Azure CLI for CAE
az containerapp logs show \
  --name keycloak \
  --resource-group mic-erp-be-dev-apps-rg \
  --follow

Internal Documentation — Microtec Platform Team