Skip to content
Back to skills

Keycloak

BSecurity

Expert guidance for Keycloak identity and access management including realm configuration, client setup, user federation, authentication flows, role-based access control, and integration with applications. Use this when setting up authentication, configuring SSO, managing users and roles, or integrating Keycloak with applications.

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 9, 2026
developmentjavascripttypescriptgojavabashsqlreactnextjsnodeexpress

Works with

  • cli
  • api

Security analysis

B84/100
  • mediumUses curl or wget to download content
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill keycloak --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Keycloak?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Keycloak
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-keycloak/badge)](https://www.skillsdirectory.com/skills/majiayu000-keycloak)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Keycloak
description: Expert guidance for Keycloak identity and access management including realm configuration, client setup, user federation, authentication flows, role-based access control, and integration with applications. Use this when setting up authentication, configuring SSO, managing users and roles, or integrating Keycloak with applications.
---

# 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)
```bash
# 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
```yaml
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
```bash
# 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: noreply@example.com
  - 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: john@example.com
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
```java
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
```bash
# 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
```bash
# 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": "john@example.com",
    "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
```typescript
// 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
```javascript
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
```typescript
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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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

- Docs: https://www.keycloak.org/documentation
- Admin REST API: https://www.keycloak.org/docs-api/latest/rest-api/
- Server Admin Guide: https://www.keycloak.org/docs/latest/server_admin/
- GitHub: https://github.com/keycloak/keycloak

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.