Skip to content
Menu
DevSecOps Now!!!
  • About
  • Certifications
  • Contact
  • Courses
  • DevSecOps Consulting
  • DevSecOps Tools
  • Training
  • Tutorials
DevSecOps Now!!!

Keycloak Client Scopes & Mappers — Step‑by‑Step, Hands‑On Tutorial

Posted on September 27, 2025

Limited Time Offer!

For Less Than the Cost of a Starbucks Coffee, Access All DevOpsSchool Videos on YouTube Unlimitedly.
Master DevOps, SRE, DevSecOps Skills!

Enroll Now

What you’ll learn

  • The purpose of Client Scopes and how mappers work.
  • When to use Default vs Optional client scopes.
  • How to map user properties, user attributes, roles, and groups into OIDC tokens.
  • How to build nested JSON claims, scripted claims, and hardcoded claims.
  • How to test with the Authorization Code + PKCE flow and validate claims.
  • Best practices, security notes, troubleshooting, and CLI automation.

Applies to: Keycloak 20+ (and newer). UI labels may differ slightly across versions, but concepts and steps remain the same.


Quick mental model

  • Client = An application that asks for tokens.
  • Client Scope = A reusable bundle of mappers.
  • Mapper = A rule that maps information (from Keycloak’s user model/roles/groups/etc.) into token claims.
  • Claim = A field in the ID Token / Access Token / UserInfo response.
flowchart LR
  A[User authenticates] --> B[Client requests tokens]
  B --> C[Keycloak]
  C --> D[Evaluate Client Scopes]
  D --> E[Run Mappers]
  E --> F[Produce Claims in Tokens]
  F --> G[Application consumes tokens]

Prerequisites

  • A Keycloak realm (e.g., wizbrand).
  • An OIDC confidential or public client (e.g., wizbrand-web).
  • A test user who can log in.
  • Ability to reach the realm’s Authorization Endpoint and Token Endpoint (for testing).

Tip: Use a dedicated dev/test realm so you can experiment safely.


Core concepts

Client Scopes

  • Default Client Scopes: Automatically applied to a client’s token requests.
  • Optional Client Scopes: Only applied when explicitly requested via the scope parameter in the OIDC request (e.g., scope=openid profile email my-optional-scope).

Mappers (OIDC protocol)

Common mapper types you’ll use:

  1. User Property — maps built‑in user fields (username, email, first/last name).
  2. User Attribute — maps custom attributes you add to users (e.g., department, tenantId).
  3. Group Membership — adds user’s groups as a claim (optionally with full path).
  4. Role mappers — project roles into custom claims (beyond the default realm_access/resource_access).
  5. Script Mapper — generate claims with custom logic.
  6. Hardcoded claim — inject a fixed value (useful for markers/flags).

Where claims appear

  • ID Token: for the client (frontend) to know who the user is.
  • Access Token: for APIs to authorize requests.
  • UserInfo: fetched via the UserInfo endpoint using the Access Token.

Only enable claims in the places you actually need them.


Lab 0 — One‑time setup

Goal: A working client and user to test with.

  1. Create/verify realm: Realm name: wizbrand.
  2. Create clientwizbrand-web:
    • Protocol: OpenID Connect
    • Access type: Public (for SPA) or Confidential (for server/web app)
    • Valid redirect URIs: http://localhost:3000/*, http://localhost:8080/* (adjust for your app)
    • Standard flow: ON (Authorization Code)
    • PKCE: ON for public/SPA
  3. Create user rajesh with a password, verify Email Verified if you plan to expose email claim.

If using a confidential client, note the client secret for token requests.


Lab 1 — Create a reusable Client Scope with basic profile claims

Goal: Build a scope that adds core identity fields in a standard way.

  1. Create Client Scope
    • Name: app-common
    • Protocol: OpenID Connect
  2. Add mappers
    • User Property → email
      • Token Claim Name: email
      • Include in ID token: ✅ Include in Access token: ➖ (only if your APIs need it)
      • Add to userinfo: ✅
    • User Property → given_name(first name)
      • Token Claim Name: given_name
      • ID token ✅, UserInfo ✅
    • User Property → family_name(last name)
      • Token Claim Name: family_name
      • ID token ✅, UserInfo ✅

You could alternatively attach Keycloak’s built‑in profile/email scopes, but creating your own makes intent explicit and avoids surprises.

  1. Attach the scope to the client
    • Client → Client scopes → Add as Default → select app-common.
  2. Test
    • Log in via your app or use a test tool.
    • Decode the ID Token at https://jwt.io (offline) or inspect in your app.
    • You should see:
{
  "email": "rajesh@example.com",
  "given_name": "Rajesh",
  "family_name": "Kumar"
}

Lab 2 — Add Groups to tokens (RBAC via groups)

Goal: Add groups claim to tokens for group‑based authorization.

  1. Create groups
    • hospital
    • doctor
    • patient
      Optionally nest them: /wizbrand/hospital, /wizbrand/doctor, /wizbrand/patient.
  2. Assign user rajesh to one or more groups.
  3. Add Group Membership mapper to app-common
    • Mapper Type: Group Membership
    • Token Claim Name: groups
    • Full group path: ON (so claim shows /wizbrand/hospital instead of just hospital)
    • ID token: ✅ Access token: ✅ UserInfo: ✅
  4. Test
    Expect something like:
{
  "groups": [
    "/wizbrand/hospital"
  ]
}

Tip: Many libraries expect groups in a flat string array. Keep the claim as String type. If you don’t want the full path, turn that option OFF.


Lab 3 — Expose roles in a convenient claim

Goal: Make roles easy for your app/API to consume.

By default, roles appear under:

{
  "realm_access": { "roles": ["admin", "user"] },
  "resource_access": {
    "wizbrand-web": { "roles": ["writer", "reader"] }
  }
}

If your app prefers a flat claim (e.g., roles: ["admin","writer"]), add a mapper.

  1. Create a new mapper in app-common
    • Mapper Type: Role name mapper (or equivalent “Aggregate realm/client roles into claim” in your KC version)
    • Token Claim Name: roles
    • Claim JSON Type: String (KC will make an array)
    • Realm roles and Client roles: Include as per your need
    • ID token: ➖ Access token: ✅ (APIs usually need roles)
  2. Test
{
  "roles": ["admin","writer"]
}

Note: If you’re happy to consume realm_access/resource_access directly, you can skip this mapper.


Lab 4 — Custom user attributes (department, tenant, tier)

Goal: Add business metadata to tokens.

  1. Add attributes to user rajesh:
    • department = Finance
    • tenantId = wiz-001
    • tier = pro
  2. Add mappers (to app-common)
    • Mapper Type: User Attribute
    • User Attribute: department
    • Token Claim Name: app.department
    • Claim JSON Type: String
    • ID token ✅, Access token ✅
    Repeat for tenantId → app.tenantId, and tier → app.tier.
  3. Result (KC uses dotted claim names to build nested JSON):
{
  "app": {
    "department": "Finance",
    "tenantId": "wiz-001",
    "tier": "pro"
  }
}

Tip: Namespacing your custom claims (e.g., under app.* or https://wizbrand.com/claims/*) avoids collisions with standard OIDC fields.


Lab 5 — Scripted claim (dynamic logic)

Goal: Compute a claim based on conditions.

  1. Add a Script Mapper to app-common
    • Token Claim Name: app.is_admin
    • Script (example):
// available: user, realm, clientSession, session
// returns the value to place in the claim
var hasAdmin = user.getRoleMappings().stream()
  .anyMatch(function(r) { return r.getName() === 'admin'; });
hasAdmin
  • Claim JSON Type: boolean
  • ID token ➖, Access token ✅
  1. Test
{
  "app": {
    "is_admin": true
  }
}

Caution: Script mappers run on the auth path. They add latency and can impact throughput. Prefer static mappers when possible.


Lab 6 — Hardcoded claim (markers/flags)

Goal: Stamp a fixed marker into tokens.

  1. Add Hardcoded Claim to app-common
    • Token Claim Name: app.license
    • Claim value: enterprise
    • Claim JSON Type: String
    • ID token ✅, Access token ✅

Result

{
  "app": { "license": "enterprise" }
}

Attaching scopes: Default vs Optional

  • Default: Always applied to the client (no work from the caller).
  • Optional: Only applied when requested with scope.

Example OIDC request (Authorization Code + PKCE):

GET /realms/wizbrand/protocol/openid-connect/auth
  ?client_id=wizbrand-web
  &response_type=code
  &redirect_uri=http://localhost:8080/callback
  &scope=openid app-common app-advanced
  &code_challenge=...
  &code_challenge_method=S256

If app-advanced is an Optional client scope, it will be included only when present in scope.


Test the tokens end‑to‑end

1) Get an authorization code

Use your app or a tool (like OAuth 2.0 client in Postman) to perform the Authorization Code + PKCE flow.

2) Exchange code for tokens

POST /realms/wizbrand/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

client_id=wizbrand-web
&grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=http://localhost:8080/callback
&code_verifier=CODE_VERIFIER

3) Inspect tokens

  • Decode ID Token and Access Token (JWT) at jwt.io (offline), or
  • Call UserInfo with the Access Token:
GET /realms/wizbrand/protocol/openid-connect/userinfo
Authorization: Bearer ACCESS_TOKEN

Confirm your claims are present where you expect them.


Automate with kcadm.sh (CLI)

Run from Keycloak’s bin/ directory or wherever kcadm.sh is available.

  1. Login
kcadm.sh config credentials --server http://localhost:8080/auth \
  --realm master --user admin --password 'admin'
  1. Create client scope
kcadm.sh create client-scopes -r wizbrand -s name=app-common -s protocol=openid-connect
  1. Add User Attribute mapper (department → app.department)
SCOPE_ID=$(kcadm.sh get client-scopes -r wizbrand -q name=app-common --fields id --format csv | tail -n1)

kcadm.sh create protocol-mappers/models -r wizbrand \
  -s name=department \
  -s protocol=openid-connect \
  -s protocolMapper=oidc-usermodel-attribute-mapper \
  -s 'config."user.attribute"=department' \
  -s 'config."claim.name"=app.department' \
  -s 'config."jsonType.label"=String' \
  -s 'config."id.token.claim"=true' \
  -s 'config."access.token.claim"=true' \
  -s 'config."userinfo.token.claim"=true' \
  -r wizbrand client-scopes/$SCOPE_ID/protocol-mappers/models
  1. Attach as Default to client
CLIENT_ID=$(kcadm.sh get clients -r wizbrand -q clientId=wizbrand-web --fields id --format csv | tail -n1)

kcadm.sh update clients/$CLIENT_ID/default-client-scopes/$SCOPE_ID -r wizbrand

Similar commands can create group/role/script/hardcoded mappers. Use the appropriate protocolMapper ids (e.g., oidc-group-membership-mapper, oidc-hardcoded-claim-mapper, oidc-script-based-protocol-mapper).


Best practices checklist

  • Minimize PII and sensitive data in tokens; prefer UserInfo for sensitive fields.
  • Keep tokens small (< 8 KB). Large tokens cause header bloat and gateway/proxy issues.
  • Namespace custom claims (app.* or https://your-domain/claims/*).
  • Prefer Default scopes for must‑have claims; Optional scopes for sometimes‑needed data.
  • Enable only where needed (ID token vs Access token vs UserInfo) to reduce exposure.
  • Avoid heavy Script mappers on high‑traffic realms; precompute attributes where possible.
  • Rotate keys and validate JWT signatures in apps/APIs.
  • Cache aware: Tokens won’t reflect user changes until the next login/refresh by default.

Troubleshooting

  • My claim doesn’t show up
    • Is the mapper in a scope that’s attached to the client?
    • If it’s Optional, did you request it via scope?
    • Did you enable ID token / Access token / UserInfo in the mapper?
    • Does the user actually have the attribute/role/group?
    • Is Claim JSON Type correct (e.g., boolean vs String)?
    • Are you decoding the right token (ID vs Access)?
    • Did you log out or refresh token after changes?
  • Groups appear with slashes (e.g., /wizbrand/hospital)
    • Disable Full group path if you want just the leaf names.
  • APIs don’t see claims
    • Ensure the claim is in the Access Token, not only in the ID Token.
  • Token too big
    • Reduce claims; use UserInfo endpoint instead; or move to reference tokens (with token introspection).

Security notes

  • Don’t put secrets or internal IDs that should remain server‑side into tokens.
  • Prefer opaque/reference tokens for critical APIs behind an introspection policy.
  • Comply with privacy regulations (GDPR/DPDP): only expose necessary claims with explicit consent where applicable.

Design patterns & FAQs

Q: Roles vs Groups?

  • Groups model membership (departments, tenants), often mapped to permissions via policy.
  • Roles express permissions directly. Many teams use groups → roles mapping in authorization policies.

Q: OIDC vs SAML mappers?

  • The idea is the same, but mapper types differ. Choose protocol accordingly in the scope.

Q: Multi‑tenant apps?

  • Include a tenantId claim and validate it in every request. Consider realm per tenant only if isolation requirements demand it.

Q: Where does Keycloak store session/access data in the browser?

  • The application typically stores the tokens (e.g., in memory/secure HTTP‑only cookies). Keycloak also sets cookies (e.g., KEYCLOAK_SESSION, AUTH_SESSION_ID) under your realm/host during flows, but apps are responsible for long‑term storage of tokens.

Q: Using Laravel Socialite—where are tokens saved?

  • Socialite returns tokens to your Laravel app; you store them (usually hashed/rotated) in session or DB per your implementation. Socialite itself does not persist them long‑term.

Appendix — Sample combined token (excerpt)

{
  "sub": "...",
  "aud": "wizbrand-web",
  "email": "rajesh@example.com",
  "given_name": "Rajesh",
  "family_name": "Kumar",
  "groups": ["/wizbrand/hospital"],
  "roles": ["admin","writer"],
  "app": {
    "department": "Finance",
    "tenantId": "wiz-001",
    "tier": "pro",
    "is_admin": true,
    "license": "enterprise"
  },
  "realm_access": {"roles": ["admin","user"]},
  "resource_access": {"wizbrand-web": {"roles": ["writer","reader"]}}
}

Next steps

  • Extract these steps into Terraform/Ansible for reproducible IAM.
  • Add policy‑enforcement in your API gateway or microservices using these claims.
  • Create a playground realm where teammates can practice mapper patterns safely.

Post Views: 7,621
  • how mappers work
  • how to create client scope
  • how to create client scope in keycloak
  • Keycloak
  • Keycloak Client Scopes
  • Keycloak Client Scopes & Mappers
  • Keycloak Mappers
  • Mappers
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
  • Mautic and PHP 8.3 Compatibility Guide (2026)
  • Certified AIOps Engineer: The Complete Career Path and Certification Guide
  • How to Rename Apache Virtual Host Files Safely (Step-by-Step Guide for Linux)
  • AIOps Foundation Certification: Everything You Need to Know to Get Certified
  • DevOps to Certified Site Reliability Professional: A Senior Mentor’s Guide
  • Certified Site Reliability Manager Training, Preparation, and Career Mapping
  • Certified Site Reliability Architect: The Complete Career Guide
  • What Is a VPN? A Complete Beginner-to-Advanced Tutorial
  • How to Install, Secure, and Tune MySQL 8.4 on Ubuntu 24.04 for Apache Event MPM and PHP-FPM
  • Complete Guide to Certified Site Reliability Engineer Career
  • Certified DevSecOps Professional Step by Step
  • Certified DevSecOps Manager: Complete Career Guide
  • Certified DevSecOps Engineer: Skills, Career Path and Certification Guide
  • Step-by-Step: Become a Certified DevSecOps Architect
  • Tuning PHP 8.3 for Apache Event MPM and PHP-FPM on Ubuntu: A Complete Step-by-Step Production Guide
  • Complete Step-by-Step Guide to Configure Apache Event MPM, Create index.php, Set Up VirtualHost, and Fix Ubuntu Default Page
  • Convert XAMPP Apache to Event MPM + System PHP-FPM
  • The Gateway to System Observability Engineering (MOE)
  • How to Finetune Apache and Prove It Works: A Real-World Guide to Testing Performance, Concurrency, HTTP/2, Memory, CPU, and Security
  • Building a High-Performance Apache Event MPM + PHP-FPM + MariaDB Stack (Advanced Server Optimization Guide)
  • Master Infrastructure as Code: The Complete Hashicorp Terraform Associate Guide
  • Building a High-Performance Apache Server with Event MPM + PHP-FPM (Step-by-Step Guide)
  • Is XAMPP Safer for Production Than Using Apache and PHP as Root? 2026 Practical Guide
  • Unlock Cloud Security Expertise with Certified Kubernetes Security Specialist (CKS)
  • How to Fix wpDiscuz Not Replacing Default WordPress Comments in Block Themes
  • Complete Guide to Certified Kubernetes Application Developer Certification
  • Overview of Certified Kubernetes Administrator (CKA) Certification
  • How to Install and Configure XAMPP on Ubuntu 24 Server (Latest Version – 2026 Complete Guide)
  • Mastering the Google Cloud Professional DevOps Engineer
  • Mastering Azure Cloud Security: The AZ-500 Path

Recent Comments

  1. digital banking on Complete Tutorial: Setting Up Laravel Telescope Correctly (Windows + XAMPP + Custom Domain)
  2. SAHIL DHINGRA on How to Uninstall Xampp from your machine when it is not visible in Control panel programs & Feature ?
  3. Abhishek on MySQL: List of Comprehensive List of approach to secure MySQL servers.
  4. Kristina on Best practices to followed in .httacess to avoid DDOS attack?
  5. Roshan Jha on Git all Commands

Archives

  • April 2026
  • March 2026
  • February 2026
  • January 2026
  • December 2025
  • November 2025
  • October 2025
  • September 2025
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022

Categories

  • Ai
  • AI Blogging
  • AiOps
  • ajax
  • Android Studio
  • Antimalware
  • Antivirus
  • Apache
  • Api
  • API Security
  • Api Testing
  • APK
  • Aws
  • Bike Rental Services
  • ChatGPT
  • Code Linting
  • Composer
  • cPanel
  • Cyber Threat Intelligence
  • Cybersecurity
  • Data Loss Prevention
  • Database
  • dataops
  • Deception Technology
  • DeepSeek
  • Devops
  • DevSecOps
  • DevTools
  • Digital Asset Management
  • Digital Certificates
  • Docker
  • Drupal
  • emulator
  • Encryption Tools
  • Endpoint Security Tools
  • Error
  • facebook
  • Firewalls
  • Flutter
  • git
  • GITHUB
  • Google Antigravity
  • Google play console
  • Google reCAPTCHA
  • Gradle
  • Guest posting
  • health and fitness
  • IDE
  • Identity and Access Management
  • Incident Response
  • Instagram
  • Intrusion Detection and Prevention Systems
  • jobs
  • Joomla
  • Keycloak
  • Laravel
  • Law News
  • Lawyer Discussion
  • Legal Advice
  • Linkedin
  • Linkedin Api
  • Linux
  • Livewire
  • Mautic
  • Medical Tourism
  • MlOps
  • MobaXterm
  • Mobile Device Management
  • Multi-Factor Authentication
  • MySql
  • Network Traffic Analysis tools
  • Paytm
  • Penetration Testing
  • php
  • PHPMyAdmin
  • Pinterest Api
  • Quora
  • SAST
  • SecOps
  • Secure File Transfer Protocol
  • Security Analytics Tools
  • Security Auditing Tools
  • Security Information and Event Management
  • Seo
  • Server Management Tools
  • Single Sign-On
  • Site Reliability Engineering
  • soft 404
  • software
  • SuiteCRM
  • SysOps
  • Threat Model
  • Twitter
  • Twitter Api
  • ubuntu
  • Uncategorized
  • Virtual Host
  • Virtual Private Networks
  • VPNs
  • Vulnerability Assessment Tools
  • Web Application Firewalls
  • Windows Processor
  • Wordpress
  • WSL (Windows Subsystem for Linux)
  • X.com
  • Xampp
  • Youtube
©2026 DevSecOps Now!!! | WordPress Theme: EcoCoded
wpDiscuz