Configure Keycloak realm/client settings, integrate Auth.js v5 OIDC in the web portal (project.json → mainApp), set Keycloak Helm values (init/full stage), recover bootstrap admin, or debug KC_HOSTNAME/issuer mismatches. Do not use for Helm chart templating (use helm-chart-expert), GCP firewall rules (use kubernetes-deployment), or database migrations (use migration-check).
Scanned 9/5/2026
Install to Claude Code
npx -y skills add atretyak1985/swarmery --skill keycloak --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Keycloak?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/atretyak1985-keycloak)More formats (shields.io, HTML) on the badges page.
---
name: keycloak
description: "Configure Keycloak realm/client settings, integrate Auth.js v5 OIDC in the web portal (project.json → mainApp), set Keycloak Helm values (init/full stage), recover bootstrap admin, or debug KC_HOSTNAME/issuer mismatches. Do not use for Helm chart templating (use helm-chart-expert), GCP firewall rules (use kubernetes-deployment), or database migrations (use migration-check)."
version: "1.0.0"
owner: "swarmery-infra"
allowed-tools: Read, Bash, Write, Edit, Grep, Glob, WebFetch, WebSearch
docs:
status: reviewed
source_sha: 47cf71e1a20f
updated: 2026-08-06
---
# Purpose
Provide Keycloak identity and access management patterns for the platform. Cover realm setup, client configuration for Auth.js v5 in the web portal repo (project.json → mainApp), two-stage Helm deployment (init/full), bootstrap admin recovery, issuer mismatch fixes, and NetworkPolicy requirements. For Helm chart template authoring beyond Keycloak values, defer to `helm-chart-expert`. For GCP firewall or ingress debugging, defer to `kubernetes-deployment`.
# When to use
- Configuring the `<keycloak-realm>` realm, clients, roles, or groups in Keycloak
- Integrating Auth.js v5 with Keycloak OIDC in the web portal
- Setting up Keycloak Helm values (init stage or full stage with ingress)
- Debugging Auth.js `error=Configuration` or issuer mismatch (`iss` claim)
- Recovering from bootstrap admin failures (existing database, OOM-kill)
- Configuring NetworkPolicy for ingress-to-Keycloak traffic
# When NOT to use
- Authoring Helm chart templates or `_helpers.tpl` -- use `helm-chart-expert`
- Debugging GCP firewall rules or minikube tunnel -- use `kubernetes-deployment`
- Checking migration safety -- use `migration-check`
- Detecting IaC config drift -- use `infrastructure-as-code`
# Required environment
- Runtime: `.claude/skills/keycloak/SKILL.md`
- Tools: `kubectl`, `helm`, `bash`
- Keycloak version: 26.x (codecentric/keycloakx Helm chart)
- Setup script: the infrastructure repo's `files/keycloak/setup-keycloak.sh` (or the project's equivalent)
- Namespace: environment-specific (`<infra-namespace>`)
# Inputs
- `operation: enum` -- one of: `realm-setup`, `auth-integration`, `debug-auth`, `bootstrap-recovery`, `helm-values`
- `environment: string` -- target environment (localdev, `<envAlias>`, prod)
- `symptom: string` (optional) -- error message or behavior being debugged
# Outputs
- Format: configuration YAML, Auth.js TypeScript code, or a diagnostic command sequence
- Length budget: max 150 lines per response; for multi-operation tasks, produce one section per operation
- For `debug-auth`: ordered diagnosis steps with specific kubectl/curl commands
- For `bootstrap-recovery`: step-by-step recovery procedure with safety warnings
# Procedure
1. **Identify operation and environment** -- Confirm which Keycloak operation is needed and which environment.
**Checkpoint:** Operation type determined.
2. **Check prerequisites** -- Verify Keycloak pod is running (`kubectl get pods -n $NAMESPACE | grep keycloak`). For auth debugging, verify both internal DNS and ingress are reachable.
**Checkpoint:** Keycloak pod status confirmed.
3. **Apply configuration** -- Use the appropriate pattern (Helm values, setup script, or Auth.js config).
**Checkpoint:** Config applied or code change suggested.
4. **Verify** -- For auth integration, confirm the OIDC discovery endpoint is reachable and the issuer URL matches `AUTH_KEYCLOAK_ISSUER`. Run: `curl <issuer>/.well-known/openid-configuration` and verify the JSON `issuer` field.
**Checkpoint:** Discovery endpoint returns valid JSON with matching issuer.
# Self-check
- [ ] No plaintext passwords -- used `$KC_ADMIN_PASSWORD` or equivalent placeholder
- [ ] Issuer URL matches the `AUTH_KEYCLOAK_ISSUER` env var format
- [ ] Used `kubernetes.io/metadata.name` for NetworkPolicy namespace selectors, not custom `name:` labels
- [ ] Distinguished between `KC_HOSTNAME` (routing) and `KC_HOSTNAME_URL` (token issuer) when both are needed
- [ ] Verified whether the database is fresh before suggesting bootstrap admin vars will work
- [ ] Did not suggest running `kc.sh bootstrap-admin user` inside a pod (causes OOM-kill)
# Common mistakes
- DO NOT hardcode admin passwords in values examples -- use `$KC_ADMIN_PASSWORD` placeholder with a `# LOCAL DEV ONLY` comment
- DO NOT use `name: ingress-nginx` as a namespace selector label -- minikube addon does not add it; use `kubernetes.io/metadata.name: ingress-nginx`
- DO NOT run `/opt/keycloak/bin/kc.sh bootstrap-admin user` inside a running pod -- starts a second JVM, causes OOM-kill at 1Gi memory limit
- DO NOT assume `KC_BOOTSTRAP_ADMIN_*` vars work on an existing database -- they only take effect on first startup with an empty auth database
- DO NOT rely on client-side auth checks alone -- always re-verify in the route handler or server action
- DO NOT use `NEXTAUTH_URL` -- this is Auth.js v4 naming. In Auth.js v5, the env var is `AUTH_URL`
# Escalation
- STOP when: the Keycloak pod is in CrashLoopBackOff and logs show a database connection error (may need PostgreSQL debugging)
- STOP when: the user wants to modify production Keycloak security settings
- STOP when: the issuer mismatch involves a certificate trust chain issue
# Examples
## Example: Two-stage Helm deployment
**Stage 1 -- Init (bootstrap without ingress):**
```yaml
# values.init.localdev.yaml
keycloak:
enabled: true
command: ["/opt/keycloak/bin/kc.sh"]
args: ["start-dev"]
database:
vendor: postgres
hostname: <infra-release>-postgresql.$NAMESPACE.svc.cluster.local
port: 5432
database: user_access
existingSecret: <infra-release>-postgresql
existingSecretKey: postgres-password
extraEnv: |
- name: KC_BOOTSTRAP_ADMIN_USERNAME
value: admin
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
value: "$KC_ADMIN_PASSWORD" # LOCAL DEV ONLY -- never use in staging/prod
```
**Stage 2 -- Full (add ingress + hostname):**
```yaml
# values.localdev.yaml
keycloak:
enabled: true
extraEnv: |
- name: KC_HOSTNAME
value: "keycloak.<localdev-host>"
- name: KC_HOSTNAME_STRICT
value: "false"
ingress:
enabled: true
ingressClassName: nginx
rules:
- host: "keycloak.<localdev-host>"
paths:
- path: /
pathType: Prefix
```
## Example: Auth.js v5 integration in the web portal
```typescript
// <mainApp>/src/auth.ts
import NextAuth from "next-auth";
import Keycloak from "next-auth/providers/keycloak";
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Keycloak({
clientId: process.env.KEYCLOAK_CLIENT_ID!,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
issuer: process.env.KEYCLOAK_ISSUER!,
}),
],
callbacks: {
async jwt({ token, account, profile }) {
if (account) {
token.accessToken = account.access_token;
token.roles = (profile as { realm_access?: { roles?: string[] } })
?.realm_access?.roles ?? [];
}
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken as string | undefined;
session.roles = (token.roles as string[] | undefined) ?? [];
return session;
},
},
});
```
Required env vars: `AUTH_SECRET`, `AUTH_URL`, `KEYCLOAK_CLIENT_ID`, `KEYCLOAK_CLIENT_SECRET`, `KEYCLOAK_ISSUER`.
Note: `AUTH_URL` replaces the Auth.js v4 `NEXTAUTH_URL`. Do not use `NEXTAUTH_URL` in Auth.js v5 projects.
## Example: Diagnosing issuer mismatch (error=Configuration)
**Symptom:** Auth.js callback returns `error=Configuration`. Browser shows redirect loop.
**Cause:** the web portal pod exchanges tokens via internal cluster DNS (`<infra-release>-keycloak-http.$NAMESPACE.svc.cluster.local`). Keycloak embeds that internal URL as the `iss` claim. Auth.js rejects it because `iss` does not match `KEYCLOAK_ISSUER` (the public HTTPS URL).
**Fix:** Add `KC_HOSTNAME_URL` to Keycloak env vars:
```yaml
keycloak:
extraEnv: |
- name: KC_HOSTNAME
value: "keycloak.staging.example.com"
- name: KC_HOSTNAME_URL
value: "https://keycloak.staging.example.com"
- name: KC_HOSTNAME_STRICT
value: "true"
```
- `KC_HOSTNAME` controls request routing and admin console hostname
- `KC_HOSTNAME_URL` controls the full base URL in JWT `iss` claims and OIDC discovery
- Both are needed when internal and external URLs differ
## Example: Bootstrap admin recovery (existing database)
`KC_BOOTSTRAP_ADMIN_*` vars only take effect on first startup with an empty database.
**Recovery procedure:**
```bash
# 1. Scale Keycloak to 0
kubectl scale deployment <infra-release>-keycloak -n "$NAMESPACE" --replicas=0
# 2. Drop and recreate the auth database
kubectl exec -n "$NAMESPACE" <infra-release>-postgresql-0 -- \
psql -U postgres -c "DROP DATABASE user_access;"
kubectl exec -n "$NAMESPACE" <infra-release>-postgresql-0 -- \
psql -U postgres -c "CREATE DATABASE user_access;"
# 3. Scale Keycloak back up (bootstrap vars now take effect)
kubectl scale deployment <infra-release>-keycloak -n "$NAMESPACE" --replicas=1
# 4. Re-run setup script to recreate realms, clients, users
./files/keycloak/setup-keycloak.sh local
```
# Failure modes
| Mode | Symptom | Fix |
|------|---------|-----|
| error=Configuration | Redirect loop or error page after login | Decode JWT, check `iss` vs `KEYCLOAK_ISSUER`; add `KC_HOSTNAME_URL` |
| Bootstrap admin fails | Keycloak starts but admin login fails, no error in logs | Check if the auth DB has existing data; drop/recreate DB |
| NetworkPolicy blocks ingress | Keycloak unreachable via ingress but port-forward works | Use `kubernetes.io/metadata.name: ingress-nginx` selector |
| TLS 308 redirect timeout | `ERR_CONNECTION_TIMED_OUT` on HTTPS, HTTP works | Check GCP firewall for `allow-https` rule; see `kubernetes-deployment` |
# Related skills
- `helm-chart-expert` -- defer for Helm template authoring; compose when Keycloak values need template-level changes
- `kubernetes-deployment` -- defer for GCP firewall, minikube tunnel, and ingress debugging
- `infrastructure-as-code` -- defer for drift detection; compose when Keycloak Helm values override was applied manually
- `migration-check` -- no direct overlap; Keycloak manages its own database schema
# How to use
## What it does
This skill gives you working Keycloak patterns for a Kubernetes platform: realm and client setup, Auth.js v5 OIDC wiring in the web portal, two-stage Helm values (init then full with ingress), bootstrap admin recovery, and the `KC_HOSTNAME` vs `KC_HOSTNAME_URL` distinction that causes most issuer mismatches. It knows the traps — OOM-killed bootstrap commands, NetworkPolicy selectors that silently drop ingress traffic, v4 env var names in a v5 project.
## When to use it
- Login fails with `error=Configuration` or a redirect loop, and you suspect the `iss` claim.
- You are adding Keycloak sign-in to the web portal with Auth.js v5 and need the provider, callbacks, and env vars.
- You need Keycloak Helm values for a fresh environment, either the init stage or the full stage with ingress.
- The admin account no longer works on an existing database and you need a safe recovery path.
## When not to use it
- Authoring Helm chart templates or `_helpers.tpl` — use `helm-chart-expert`.
- Cloud firewall rules, tunnels, or general ingress debugging — use `kubernetes-deployment`.
- Checking whether a database migration is safe to run — use `migration-check`.
- Detecting drift between committed IaC and the live cluster — use `infrastructure-as-code`.
## How to invoke
```
Skill(skill: "infra-pack:keycloak")
```
Invoke it, then describe the operation and the target environment. The skill picks the matching pattern and walks the four-step procedure: identify the operation, check that the Keycloak pod is up, apply the config, then verify against the OIDC discovery endpoint.
## Inputs
- `operation` — one of `realm-setup`, `auth-integration`, `debug-auth`, `bootstrap-recovery`, `helm-values` — required.
- `environment` — the target environment, such as localdev, `<envAlias>`, or prod — required.
- `symptom` — the error message or observed behavior you are debugging — optional.
## What you get back
Configuration YAML, Auth.js TypeScript, or an ordered sequence of `kubectl`/`curl` commands — capped at about 150 lines, one section per operation. Debugging answers come as ranked diagnosis steps; recovery answers come as a numbered procedure with safety warnings. Passwords always appear as placeholders such as `$KC_ADMIN_PASSWORD`, never as literals.
## Worked example
```
Skill(skill: "infra-pack:keycloak")
"Login on <envAlias> returns error=Configuration after the callback.
operation: debug-auth, environment: <envAlias>"
```
The skill has you decode the JWT and compare its `iss` claim against `KEYCLOAK_ISSUER`. The pod exchanges tokens over internal cluster DNS, so Keycloak stamps the internal URL into `iss` and Auth.js rejects it. You get back Helm `extraEnv` adding `KC_HOSTNAME_URL` with the public HTTPS base URL alongside `KC_HOSTNAME`, plus a `curl <issuer>/.well-known/openid-configuration` check to confirm the fix.
## Related
- `helm-chart-expert` — prefer it when the change is in the chart templates, not the Keycloak values.
- `kubernetes-deployment` — prefer it when Keycloak is unreachable at the network layer rather than misconfigured.
- `infrastructure-as-code` — prefer it when Helm values were changed by hand and you need to detect the drift.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!