| 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)
# 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
- Login to admin console: http://localhost:8080/admin
- Click "Create Realm" (top-left dropdown)
- Enter realm name (e.g., "myapp")
- 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:
- Go to "Credentials" tab
- Copy "Client secret"
- 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
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": "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
// 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
- Use HTTPS in production - Always enable SSL/TLS
- Strong client secrets - Use cryptographically random secrets
- Limit token lifetime - Short-lived access tokens (5-15 min)
- Refresh token rotation - Enable refresh token reuse detection
- PKCE for SPAs - Use Proof Key for Code Exchange
- Content Security Policy - Proper CSP headers
- Rate limiting - Protect against brute force
- Regular updates - Keep Keycloak up to date
- Audit logging - Enable and monitor event logs
- 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
- Check user is enabled
- Verify email is verified (if required)
- Check required actions (password reset, email verify)
- 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
- 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