Back to Skills

Keycloak

oriolrius
Updated Today
96 views
1
1
1
View on GitHub
Designdesign

About

This Claude Skill provides expert guidance for implementing Keycloak identity and access management. It helps developers configure realms, clients, authentication flows, and role-based access control. Use this skill when setting up authentication, SSO, user management, or integrating Keycloak with applications.

Quick Install

Claude Code

Recommended
Plugin CommandRecommended
/plugin add https://github.com/oriolrius/pki-manager-web
Git CloneAlternative
git clone https://github.com/oriolrius/pki-manager-web.git ~/.claude/skills/Keycloak

Copy and paste this command in Claude Code to install this skill

Documentation

Keycloak

Expert assistance with Keycloak identity and access management platform.

Overview

Keycloak is an open-source Identity and Access Management (IAM) solution providing:

  • Single Sign-On (SSO)
  • Identity brokering and social login
  • User federation (LDAP/Active Directory)
  • Standard protocols (OAuth 2.0, OpenID Connect, SAML 2.0)
  • Fine-grained authorization
  • Admin console and account management

Installation & Setup

Docker (Quick Start)

# Run Keycloak
docker run -d \
  --name keycloak \
  -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev

# With PostgreSQL
docker run -d \
  --name keycloak \
  -p 8080:8080 \
  -e KC_DB=postgres \
  -e KC_DB_URL=jdbc:postgresql://localhost/keycloak \
  -e KC_DB_USERNAME=keycloak \
  -e KC_DB_PASSWORD=password \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start

Docker Compose

version: '3'

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data

  keycloak:
    image: quay.io/keycloak/keycloak:latest
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_HOSTNAME_STRICT_HTTPS: false
      KC_HTTP_ENABLED: true
    ports:
      - "8080:8080"
    depends_on:
      - postgres

volumes:
  postgres_data:

Production Setup

# Build with PostgreSQL support
docker run \
  -e KC_DB=postgres \
  -e KC_FEATURES=token-exchange,admin-fine-grained-authz \
  -e KC_HTTP_ENABLED=true \
  -e KC_HOSTNAME_STRICT_HTTPS=false \
  quay.io/keycloak/keycloak:latest build

# Run in production mode
docker run \
  -p 8443:8443 \
  -e KC_DB=postgres \
  -e KC_DB_URL=jdbc:postgresql://postgres/keycloak \
  -e KC_DB_USERNAME=keycloak \
  -e KC_DB_PASSWORD=password \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  -e KC_HOSTNAME=auth.example.com \
  -e KC_HTTPS_CERTIFICATE_FILE=/opt/keycloak/conf/server.crt \
  -e KC_HTTPS_CERTIFICATE_KEY_FILE=/opt/keycloak/conf/server.key \
  quay.io/keycloak/keycloak:latest start

Realm Configuration

Create Realm

  1. Login to admin console: http://localhost:8080/admin
  2. Click "Create Realm" (top-left dropdown)
  3. Enter realm name (e.g., "myapp")
  4. Click "Create"

Realm Settings

Realm Settings:
- General
  - Display name: My Application
  - HTML Display name: <b>My App</b>
  - Frontend URL: https://auth.example.com (optional)

- Login
  - User registration: Enable to allow self-registration
  - Edit username: Allow users to edit username
  - Forgot password: Enable password reset
  - Remember me: Allow remember me checkbox
  - Login with email: Allow email as username

- Keys
  - Active keys for signing tokens
  - Configure providers (RSA, ECDSA, HMAC)

- Email
  - From: [email protected]
  - Host: smtp.example.com
  - Port: 587
  - Enable authentication
  - Username/Password for SMTP

- Themes
  - Login theme: keycloak (or custom)
  - Account theme: keycloak
  - Admin console theme: keycloak
  - Email theme: keycloak

- Tokens
  - Access Token Lifespan: 5 minutes
  - Refresh Token Max Reuse: 0
  - SSO Session Idle: 30 minutes
  - SSO Session Max: 10 hours

Client Configuration

Create Client

OpenID Connect Client:

Client ID: my-app
Client Protocol: openid-connect
Root URL: http://localhost:3000

Settings:
- Client authentication: ON (for confidential clients)
- Authorization: OFF (unless using fine-grained authz)
- Valid redirect URIs:
  - http://localhost:3000/*
  - http://localhost:3000/api/auth/callback/keycloak
- Valid post logout redirect URIs:
  - http://localhost:3000
- Web origins: http://localhost:3000

Capability config:
- Client authentication: ON
- Authorization: OFF
- Standard flow: ON (Authorization Code Flow)
- Direct access grants: ON (Resource Owner Password Credentials)
- Implicit flow: OFF (deprecated)
- Service accounts roles: ON (for client credentials)

Client Credentials

After creating client with authentication ON:

  1. Go to "Credentials" tab
  2. Copy "Client secret"
  3. Use in application configuration

Client Scopes

Create custom scope:
1. Clients > Client scopes > Create
2. Name: custom-scope
3. Protocol: openid-connect
4. Display on consent: OFF
5. Include in token scope: ON

Add mappers:
1. Mappers tab > Create
2. Mapper type: User Property
3. Property: email
4. Token Claim Name: email
5. Claim JSON Type: String

Assign to client:
1. Clients > [your-client] > Client scopes
2. Add available scope to Assigned default scopes

User Management

Create User

Admin Console > Users > Create user

Username: john.doe
Email: [email protected]
Email verified: ON
First name: John
Last name: Doe
Enabled: ON

Credentials:
- Set password
- Temporary: OFF (user won't be forced to change)

User Attributes

Users > [user] > Attributes

Key: department
Value: engineering

Key: employee_id
Value: EMP-12345

User Roles

1. Create roles:
   Realm roles > Create role
   - Name: admin
   - Name: user
   - Name: viewer

2. Assign to user:
   Users > [user] > Role mapping
   - Assign role: admin

User Groups

1. Create group:
   Groups > Create group
   - Name: Developers

2. Add attributes to group:
   Groups > Developers > Attributes
   - team: backend

3. Assign roles to group:
   Groups > Developers > Role mapping
   - Assign: developer role

4. Add users to group:
   Users > [user] > Groups
   - Join: Developers

Roles & Permissions

Realm Roles

Realm roles > Create role

Name: super-admin
Description: Full system access

Composite roles:
- Add child roles (admin, user, viewer)

Client Roles

Clients > [client] > Roles > Create role

Name: app-admin
Description: Application administrator

Use case: Application-specific roles

Role Mappers

Client scopes > roles > Mappers > realm roles

Add to token:
- Token Claim Name: realm_access.roles
- Claim JSON Type: String
- Add to ID token: ON
- Add to access token: ON
- Add to userinfo: ON

Authentication Flows

Browser Flow (Default)

Authentication > Flows > Browser

Steps:
1. Cookie (SSO check)
2. Kerberos (optional)
3. Forms (username/password)
   - Username password form
   - OTP form (if enabled)

Custom Authentication Flow

1. Duplicate existing flow:
   Flows > Browser > Duplicate

2. Customize:
   - Add execution
   - Set requirement (REQUIRED, ALTERNATIVE, DISABLED)

3. Bind to client:
   Clients > [client] > Advanced > Authentication flow overrides
   - Browser flow: [custom-flow]

Two-Factor Authentication

1. Enable OTP:
   Authentication > Flows > Browser
   - Add execution: OTP Form
   - Requirement: CONDITIONAL

2. Configure OTP:
   Authentication > OTP Policy
   - Type: Time-based or Counter-based
   - Algorithm: SHA1, SHA256, SHA512
   - Digits: 6
   - Period: 30 seconds

3. Users enable OTP:
   Account console > Account security > Signing in
   - Set up Authenticator Application

User Federation

LDAP Integration

User Federation > Add provider > LDAP

Connection:
- Console display name: LDAP
- Edit mode: READ_ONLY or WRITEABLE
- Sync registrations: ON
- Vendor: Active Directory, Red Hat Directory Server, etc.
- Connection URL: ldap://ldap.example.com:389
- Users DN: ou=users,dc=example,dc=com
- Bind DN: cn=admin,dc=example,dc=com
- Bind credential: password

LDAP searching and updating:
- Custom user search filter: (objectClass=person)
- Search scope: Subtree

Synchronization:
- Batch size: 1000
- Full sync period: 604800 (weekly)
- Changed users sync period: 86400 (daily)

Test connection and authentication

Custom User Storage SPI

public class CustomUserStorageProvider implements UserStorageProvider {
    @Override
    public UserModel getUserById(String id, RealmModel realm) {
        // Fetch user from custom storage
    }

    @Override
    public UserModel getUserByUsername(String username, RealmModel realm) {
        // Lookup by username
    }

    @Override
    public UserModel getUserByEmail(String email, RealmModel realm) {
        // Lookup by email
    }
}

Identity Providers

Social Login (Google)

Identity Providers > Add provider > Google

Settings:
- Client ID: [from Google Console]
- Client secret: [from Google Console]
- Default scopes: openid profile email
- Store tokens: ON
- Stored tokens readable: ON

Mappers:
- Create mapper: Import from provider
- Sync mode: Import or Force

SAML Provider

Identity Providers > Add provider > SAML

Settings:
- Service provider entity ID: my-app
- Single sign-on service URL: [from SAML provider]
- Name ID policy format: Email
- Principal type: Subject NameID
- Want AuthnRequests signed: ON

Import from URL or file for metadata

Token Configuration

Access Token

Clients > [client] > Settings > Advanced

Access Token Lifespan: 5 minutes
Client Session Idle: 30 minutes
Client Session Max: 10 hours

Include in token:
- Standard claims (sub, aud, iss, exp, iat)
- Custom claims via mappers

Refresh Token

Realm Settings > Tokens

Refresh Token Max Reuse: 0
Revoke Refresh Token: ON
SSO Session Idle: 30 minutes
SSO Session Max: 10 hours
Offline Session Idle: 30 days

Custom Claims

Client scopes > [scope] > Mappers > Create

Mapper type: User Attribute
User attribute: department
Token claim name: department
Claim JSON Type: String
Add to ID token: ON
Add to access token: ON
Add to userinfo: ON

Admin API

Get Admin Token

# Password grant
curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
  -d "client_id=admin-cli" \
  -d "username=admin" \
  -d "password=admin" \
  -d "grant_type=password"

API Examples

# Get realm
curl -X GET http://localhost:8080/admin/realms/myapp \
  -H "Authorization: Bearer $TOKEN"

# Create user
curl -X POST http://localhost:8080/admin/realms/myapp/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john",
    "email": "[email protected]",
    "enabled": true,
    "firstName": "John",
    "lastName": "Doe"
  }'

# Get users
curl -X GET http://localhost:8080/admin/realms/myapp/users \
  -H "Authorization: Bearer $TOKEN"

# Assign role
curl -X POST http://localhost:8080/admin/realms/myapp/users/{userId}/role-mappings/realm \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"id": "{roleId}", "name": "admin"}]'

Application Integration

Next.js with NextAuth

// pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import KeycloakProvider from "next-auth/providers/keycloak"

export default NextAuth({
  providers: [
    KeycloakProvider({
      clientId: process.env.KEYCLOAK_CLIENT_ID!,
      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
      issuer: process.env.KEYCLOAK_ISSUER, // http://localhost:8080/realms/myapp
    }),
  ],
  callbacks: {
    async jwt({ token, account }) {
      if (account) {
        token.accessToken = account.access_token
        token.refreshToken = account.refresh_token
      }
      return token
    },
    async session({ session, token }) {
      session.accessToken = token.accessToken
      return session
    },
  },
})

// .env.local
KEYCLOAK_CLIENT_ID=my-app
KEYCLOAK_CLIENT_SECRET=secret
KEYCLOAK_ISSUER=http://localhost:8080/realms/myapp

Node.js with keycloak-connect

const session = require('express-session')
const Keycloak = require('keycloak-connect')

const memoryStore = new session.MemoryStore()
const keycloak = new Keycloak({ store: memoryStore }, {
  'realm': 'myapp',
  'auth-server-url': 'http://localhost:8080',
  'ssl-required': 'external',
  'resource': 'my-app',
  'credentials': {
    'secret': 'client-secret'
  }
})

app.use(session({
  secret: 'session-secret',
  resave: false,
  saveUninitialized: true,
  store: memoryStore
}))

app.use(keycloak.middleware())

// Protected route
app.get('/protected', keycloak.protect(), (req, res) => {
  res.json({ message: 'Protected resource' })
})

// Role-based protection
app.get('/admin', keycloak.protect('admin'), (req, res) => {
  res.json({ message: 'Admin resource' })
})

React SPA

import Keycloak from 'keycloak-js'

const keycloak = new Keycloak({
  url: 'http://localhost:8080',
  realm: 'myapp',
  clientId: 'my-app',
})

// Initialize
keycloak.init({
  onLoad: 'login-required',
  checkLoginIframe: false,
}).then((authenticated) => {
  if (authenticated) {
    console.log('User is authenticated')
    console.log('Token:', keycloak.token)
    console.log('Roles:', keycloak.realmAccess?.roles)
  }
})

// Auto-refresh token
keycloak.onTokenExpired = () => {
  keycloak.updateToken(30)
}

// API call with token
fetch('/api/data', {
  headers: {
    'Authorization': `Bearer ${keycloak.token}`
  }
})

// Logout
keycloak.logout({ redirectUri: 'http://localhost:3000' })

// Check role
if (keycloak.hasRealmRole('admin')) {
  // Show admin features
}

Security Best Practices

  1. Use HTTPS in production - Always enable SSL/TLS
  2. Strong client secrets - Use cryptographically random secrets
  3. Limit token lifetime - Short-lived access tokens (5-15 min)
  4. Refresh token rotation - Enable refresh token reuse detection
  5. PKCE for SPAs - Use Proof Key for Code Exchange
  6. Content Security Policy - Proper CSP headers
  7. Rate limiting - Protect against brute force
  8. Regular updates - Keep Keycloak up to date
  9. Audit logging - Enable and monitor event logs
  10. Role hierarchy - Use composite roles for complexity

Troubleshooting

Token Validation Issues

# Decode JWT token
echo $TOKEN | cut -d. -f2 | base64 -d | jq

# Verify token signature
curl http://localhost:8080/realms/myapp/protocol/openid-connect/certs

Connection Issues

# Check Keycloak health
curl http://localhost:8080/health

# Check realm endpoints
curl http://localhost:8080/realms/myapp/.well-known/openid-configuration

User Login Issues

  1. Check user is enabled
  2. Verify email is verified (if required)
  3. Check required actions (password reset, email verify)
  4. Review authentication logs (Events > Login Events)

CORS Issues

Clients > [client] > Settings
- Web origins: http://localhost:3000
- Valid redirect URIs: http://localhost:3000/*

Common Tasks

Export/Import Realm

# Export realm
docker exec keycloak /opt/keycloak/bin/kc.sh export \
  --dir /tmp/export \
  --realm myapp

# Import realm
docker exec keycloak /opt/keycloak/bin/kc.sh import \
  --file /tmp/export/myapp-realm.json

Backup Database

# PostgreSQL backup
docker exec postgres pg_dump -U keycloak keycloak > keycloak-backup.sql

# Restore
docker exec -i postgres psql -U keycloak keycloak < keycloak-backup.sql

Theme Customization

themes/
└── custom-theme/
    ├── login/
    │   ├── theme.properties
    │   ├── login.ftl
    │   └── resources/
    │       ├── css/
    │       └── img/
    └── account/
        └── ...

Realm Settings > Themes > Login theme: custom-theme

Resources

GitHub Repository

oriolrius/pki-manager-web
Path: .claude/skills/keycloak
certificate-authoritycertificate-managementcosmianfastifykmspki

Related Skills

content-collections

Meta

This skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.

View skill

creating-opencode-plugins

Meta

This skill provides the structure and API specifications for creating OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It offers implementation patterns for JavaScript/TypeScript modules that intercept and extend the AI assistant's lifecycle. Use it when you need to build event-driven plugins for monitoring, custom handling, or extending OpenCode's capabilities.

View skill

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill

cloudflare-turnstile

Meta

This skill provides comprehensive guidance for implementing Cloudflare Turnstile as a CAPTCHA-alternative bot protection system. It covers integration for forms, login pages, API endpoints, and frameworks like React/Next.js/Hono, while handling invisible challenges that maintain user experience. Use it when migrating from reCAPTCHA, debugging error codes, or implementing token validation and E2E tests.

View skill