Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

KeycloakIdentityProvider

Identifier field: Set the alias in the spec.alias field. It is required and immutable once set. An alias inside spec.definition is tolerated only when it matches spec.alias; a conflicting value is rejected.

A KeycloakIdentityProvider represents an external identity provider configuration within a Keycloak realm.

Specification

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: my-idp
spec:
  # One of realmRef or clusterRealmRef must be specified
  
  # Option 1: Reference to a namespaced KeycloakRealm
  realmRef:
    name: my-realm
  
  # Option 2: Reference to a ClusterKeycloakRealm
  clusterRealmRef:
    name: my-cluster-realm
  
  # Optional: Reference to a Secret with config values (e.g. clientId, clientSecret)
  configSecretRef:
    name: my-idp-credentials

  # Optional: Link this IdP exclusively to a KeycloakOrganization (Keycloak 26+)
  organizationRef:
    name: acme-corp
  
  # Required: Identity provider definition
  alias: my-idp
  definition:
    providerId: oidc
    enabled: true
    # ... any other properties

Status

status:
  ready: true
  status: "Ready"
  message: "Identity provider synchronized successfully"
  resourcePath: "/admin/realms/my-realm/identity-provider/instances/my-idp"
  organizationID: "12345678-1234-1234-1234-123456789abc"
  instance:
    instanceRef: my-keycloak
  realm:
    realmRef: my-realm
  conditions:
    - type: Ready
      status: "True"
      reason: Synchronized

Example

OIDC Provider

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: corporate-sso
spec:
  realmRef:
    name: my-realm
  configSecretRef:
    name: corporate-sso-credentials
  alias: corporate-sso
  definition:
    displayName: Corporate SSO
    providerId: oidc
    enabled: true
    trustEmail: true
    firstBrokerLoginFlowAlias: first broker login
    config:
      authorizationUrl: https://sso.corp.example.com/auth
      tokenUrl: https://sso.corp.example.com/token
      userInfoUrl: https://sso.corp.example.com/userinfo
      defaultScope: openid profile email
      syncMode: IMPORT

Google Provider

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: google
spec:
  realmRef:
    name: my-realm
  configSecretRef:
    name: google-idp-credentials
  alias: google
  definition:
    displayName: Sign in with Google
    providerId: google
    enabled: true
    trustEmail: true
    config:
      defaultScope: openid profile email

GitHub Provider

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: github
spec:
  realmRef:
    name: my-realm
  configSecretRef:
    name: github-idp-credentials
  alias: github
  definition:
    displayName: Sign in with GitHub
    providerId: github
    enabled: true

SAML Provider

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: saml-idp
spec:
  realmRef:
    name: my-realm
  alias: saml-idp
  definition:
    displayName: Corporate SAML
    providerId: saml
    enabled: true
    config:
      entityId: https://idp.example.com
      singleSignOnServiceUrl: https://idp.example.com/sso
      nameIDPolicyFormat: urn:oasis:names:tc:SAML:2.0:nameid-format:transient
      signatureAlgorithm: RSA_SHA256
      wantAssertionsSigned: "true"
      wantAuthnRequestsSigned: "true"

To map an identity provider to an organization, set organizationRef to a KeycloakOrganization in the same namespace and realm. The operator waits until the organization is ready, creates or updates the identity provider, and then links it through the organization’s identity-provider endpoint (POST /admin/realms/{realm}/organizations/{id}/identity-providers). On every reconcile it checks the organization’s linked providers and re-links the identity provider if the link is missing. The linked organization’s ID is recorded in status.organizationID.

Changing organizationRef to another organization unlinks the identity provider from the previous one before linking it to the new one. Removing organizationRef unlinks it and clears status.organizationID; the identity provider itself is kept.

This requires Keycloak 26.0.0 or later. Do not set organizationId or organizationLinks inside definition — both are rejected with Ready=False and reason InvalidDefinition. Keycloak 26.8 moved identity-provider-to-organization links to a many-to-many model and ignores organizationId on the identity provider representation entirely, which is why the operator uses the organization endpoints instead; they behave the same on 26.0 and 26.8. From the operator’s side an identity provider is linked to at most one organization: organizationRef is a single reference, and links to other organizations made outside the operator are left untouched. Organizations created outside the operator can be adopted with a KeycloakOrganization CR, then referenced here.

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: acme-sso
spec:
  realmRef:
    name: my-realm
  organizationRef:
    name: acme-corp
  alias: acme-sso
  definition:
    providerId: oidc
    enabled: true
    config:
      authorizationUrl: https://sso.acme.example.com/auth
      tokenUrl: https://sso.acme.example.com/token

The organization and identity provider must reference the same realm CR (realmRef or clusterRealmRef). A mismatch sets Ready=False with reason OrganizationRealmMismatch. If the organization is missing or not yet ready, the identity provider soft-waits with reason OrganizationNotReady. If Keycloak rejects the link or unlink call, the status reports reason OrganizationLinkFailed and the operator retries.

Config from Secret

To avoid storing sensitive configuration values (such as clientId and clientSecret) in plaintext in the CR, use configSecretRef to reference a Kubernetes Secret:

kubectl create secret generic corporate-sso-credentials \
  --from-literal=clientId=my-oidc-client-id \
  --from-literal=clientSecret=my-oidc-client-secret
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: corporate-sso
spec:
  realmRef:
    name: my-realm
  configSecretRef:
    name: corporate-sso-credentials
  alias: corporate-sso
  definition:
    providerId: oidc
    enabled: true
    config:
      authorizationUrl: https://sso.corp.example.com/auth
      tokenUrl: https://sso.corp.example.com/token
      defaultScope: openid profile email

Every key-value pair in the referenced Secret is merged into definition.config before the identity provider is synced to Keycloak. Secret values take precedence over values defined inline in definition.config.

The Secret must exist in the same namespace as the KeycloakIdentityProvider. When the Secret changes, the operator automatically re-reconciles the identity provider to pick up the new values.

The same configSecretRef field is available on other CRs that have definition.config. See Secret references.

Realm-to-realm brokering

To broker logins from one realm into another on the same Keycloak, point a keycloak-oidc identity provider in the target realm at a confidential KeycloakClient in the source realm. The client’s clientSecretRef Secret can be fed straight into configSecretRef, but only if its keys are named after Keycloak’s IdP config keys. The defaults (client-id / client-secret) do not match: configSecretRef merges Secret keys verbatim, so Keycloak would store them as unrelated config entries, the IdP would have no clientId / clientSecret, and the secret value would be returned unmasked by the admin API. Set clientIdKey and clientSecretKey on the client instead:

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakClient
metadata:
  name: target-broker
spec:
  realmRef:
    name: source-realm
  clientId: target-broker
  clientSecretRef:
    name: target-broker-oidc
    clientIdKey: clientId          # match Keycloak IdP config keys
    clientSecretKey: clientSecret
  definition:
    publicClient: false
    standardFlowEnabled: true
    redirectUris:
      - https://keycloak.example.com/realms/target-realm/broker/source/endpoint
---
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: target-idp-source
spec:
  realmRef:
    name: target-realm
  alias: source
  configSecretRef:
    name: target-broker-oidc
  definition:
    providerId: keycloak-oidc
    enabled: true
    config:
      issuer: https://keycloak.example.com/realms/source-realm
      authorizationUrl: https://keycloak.example.com/realms/source-realm/protocol/openid-connect/auth
      tokenUrl: https://keycloak.example.com/realms/source-realm/protocol/openid-connect/token
      jwksUrl: https://keycloak.example.com/realms/source-realm/protocol/openid-connect/certs
      useJwksUrl: "true"
      validateSignature: "true"

Both CRs can be applied together. The identity provider reports ConfigSecretError until the client has written its Secret, then reconciles automatically; rotating the client secret propagates to the identity provider the same way.

Pick the key names before the Secret is first created. Changing clientSecretKey on a client whose Secret still carries the old key fails with SecretError (key "clientSecret" not found); delete the Secret and the operator re-materialises it from Keycloak with the new key names.

Token Exchange Permission

When this identity provider should also act as a Trusted Token Issuer — i.e. clients in the realm exchange a JWT from the upstream IdP for a Keycloak token using RFC 8693 Token Exchange with subject_issuer=<alias> — Keycloak needs a fine-grained-authz policy listing which clients are allowed to do so. Without that policy, any client in the realm could perform the exchange.

spec.tokenExchange lets the operator manage that policy declaratively. Omit the field to leave Keycloak permissions untouched (default — fully opt-in). Set allowedClients to a list of clientIds in the same realm:

apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakIdentityProvider
metadata:
  name: external-issuer
spec:
  realmRef:
    name: my-realm
  alias: external-issuer
  definition:
    providerId: oidc
    enabled: true
    config:
      issuer: https://issuer.example.com
      jwksUrl: https://issuer.example.com/.well-known/jwks.json
      useJwksUrl: "true"
      validateSignature: "true"
      hideOnLoginPage: "true"
  tokenExchange:
    allowedClients:
      - my-backend-client
      - my-app-client

On reconcile, the operator:

  1. Enables fine-grained-authz on the identity provider (PUT /identity-provider/instances/{alias}/management/permissions). Keycloak auto-creates a token-exchange scope-permission on the realm-management authz resource server.
  2. Resolves each allowedClients entry to its Keycloak client UUID.
  3. Ensures a Client-type authz policy named hostzero-idp-<alias>-token-exchange exists in realm-management’s authz resource server, with clients set to the resolved UUIDs.
  4. Binds that policy to the auto-created scope-permission. The operator owns the policy list — any other policy bound manually will be replaced.

Any client not on allowedClients attempting subject_issuer=<alias> is rejected with 403 not_allowed. An empty allowedClients: [] is valid and means “deny all”.

Soft wait on referenced clients

On first apply, the identity provider and the clients it references are often siblings in the same wave. If the operator reconciles the identity provider before a referenced client exists in Keycloak, the token-exchange reconcile defers without flipping status.ready to false. status.tokenExchange.message carries the reason; the next reconcile picks up the client once it lands.

Status

status:
  ready: true
  tokenExchange:
    enabled: true
    permissionID: <UUID of the token-exchange scope-permission>
    policyID:     <UUID of the operator-managed policy>
    policyName:   hostzero-idp-<alias>-token-exchange

enabled: false with a non-empty message indicates the operator is still waiting on referenced state.

Cleanup

Deleting the identity provider also removes the operator-managed policy from realm-management’s authz resource server. The scope-permission itself is removed by Keycloak when management-permissions are toggled off (implicit on IdP delete).

Definition Properties

Common properties from Keycloak IdentityProviderRepresentation:

PropertyTypeDescription
aliasstringUnique alias (required)
displayNamestringDisplay name
providerIdstringProvider type (oidc, saml, google, etc.)
enabledbooleanWhether provider is enabled
trustEmailbooleanTrust email from provider
storeTokenbooleanStore provider tokens
configmapProvider-specific configuration

Short Names

AliasFull Name
kcidpkeycloakidentityproviders
kubectl get kcidp

Notes

  • Use configSecretRef to store sensitive values like clientId and clientSecret in a Kubernetes Secret (see Config from Secret and Secret references)
  • Use organizationRef to bind the identity provider to a KeycloakOrganization (Keycloak 26+). Do not set organizationId or organizationLinks in definition.
  • Consider using syncMode: IMPORT to import users on first login
  • Mappers must be managed via KeycloakIdentityProviderMapper. Embedding mappers inside this CR’s definition is silently ignored by Keycloak on update — the field is only consumed during realm import.