← Back to the section

You installed Keycloak, opened the admin console — and immediately got hit with a pile of unfamiliar words: realm, client, client scope, mapper, groups, roles. It's unclear what matters most, what is nested inside what, and where to begin. And until the model settles in your head, it's easy to set things up so that login works sometimes and sometimes not, roles "don't reach" the application, and it's unclear why. Let's break down the Keycloak model piece by piece, taking our time, with real-life analogies, and at the end assemble a minimal working setup for a typical pairing of "server backend + browser frontend".

Why you even need a separate server for login

Before we dig into the internals of Keycloak, it's worth understanding what pain it cures — otherwise all these realms and clients look like needless complexity.

Imagine a company has a single application. It stores users' passwords itself, checks the login itself, decides who is allowed to do what itself. As long as there is only one application — that's tolerable. But applications become two, then five. And here it begins:

  • a user has to be created separately in each application;
  • someone changed their password in one — the old one stays in the rest;
  • there's no single sign-on: you logged into one application, but you have to log in again in the next one;
  • permissions (who is admin, who is a regular user) are described differently in each one.

Keycloak is a separate server that takes all this work onto itself. It stores users for the whole company in one place, checks passwords, and can handle login via Google or a corporate account. And to applications it hands out a confirmation: "yes, this really is Ivan, and here are his permissions". Applications no longer fiddle with passwords — they only ask Keycloak and trust its answer.

Keycloak gives its answer not as a "yes/no", but as a token — a signed string that records who the user is and what they are allowed to do. The application checks the signature (it can't be forged without Keycloak's secret key) and trusts the contents without pinging Keycloak on every request. This is an important thought we'll come back to: the application reads permissions straight from the token, rather than going to Keycloak for them.

To make it clear who talks to whom, here's the big picture. In the diagram: the user works with the application, the application sends them to log in to Keycloak, gets tokens back, and then uses them to call its own API.

diagram

Now let's look at what Keycloak consists of inside.

Realm — an isolated space

The topmost concept in Keycloak is the realm. Everything else starts from it, so we'll start from it too.

Picture an apartment building. One installed Keycloak is the whole building. And a realm is a separate apartment with its own lock. Residents of one apartment physically cannot get into another: each apartment has its own keys, its own residents, its own rules. The apartments stand in one building, but they know nothing about each other.

A realm is a fully isolated space. Inside it live: its own set of users, its own applications (clients), its own roles, groups, and login settings (for example, which methods you can log in with). The main property is isolation: a user created in one realm simply does not exist in another realm. A role from one realm has no effect in another. This is not "different folders in one system" — these really are separate worlds.

Why such strict isolation? Because on a single Keycloak server you often need to keep completely different audiences that must not overlap. Typical examples:

  • a separate realm for company employees and a separate realm for external customers — they have different users and different rules;
  • a separate realm for each independent project, so they don't interfere with each other.

The diagram shows that one Keycloak holds several realms, and they are not connected: the users and roles of realm A are in no way visible in realm B.

diagram

An important detail that newcomers stumble on: Keycloak always has a built-in realm named master. There's a temptation to dump your application's users right into it — it's already there, after all. Don't do that. master is meant only for administering Keycloak itself: it holds the accounts you use to sign into the admin console and manage the other realms. You don't put your application's regular users there. For your own project you always create a new realm — for example, myapp.

Another thing worth remembering right away: all Keycloak addresses are tied to a realm. The base address by which an application finds the keys and settings of a specific realm looks like this:

https://auth.example.com/realms/myapp

This address is called the issuer (the issuer of tokens) — it's exactly what appears in every issued token as "who released me". Remember it: you'll need it when configuring the application, and each realm has its own.

Client — the application that talks to Keycloak

Inside a realm live clients. This is the second most important concept, and it often causes confusion, because the word "client" here means not a user but an application.

Where does this concept even come from? Keycloak has to somehow tell which application came to it. A browser frontend, a mobile app, and a background server service behave differently, and they can be trusted differently. To tell them apart, a record is created in the realm for each application.

A client is that record about an application inside a realm. Every application that uses this Keycloak has its own client with its own identifier (client id) — for example, myapp-frontend or myapp-backend. When an application begins the login process, it introduces itself with this identifier: "hello, Keycloak, I'm client myapp-frontend".

The key characteristic by which clients are divided into two types is whether the application can reliably keep a secret (that is, the application's own password). This determines exactly how the application proves to Keycloak that it is who it says it is.

Public client

A public client is an application that fundamentally cannot reliably hide a secret. Why can't it? Because all of its code is available to the user:

  • the code of a single-page site (React, Angular, Vue) is loaded into the browser in full — open the developer tools and read it;
  • a mobile app can be downloaded and unpacked.

Any "secret password" baked into such code is in fact visible to anyone who wants to see it, which means it is useless as a secret. That's why a public client has no secret at all — Keycloak doesn't even require one.

This is how you configure: single-page sites (React, Angular, Vue) and mobile apps.

A reasonable question comes up: if the application has no secret, how do you then protect login from being hijacked? For this a public client always uses PKCE. The idea is simple: at the very start of login the application generates a one-time secret string, keeps it to itself, and presents it at the end, proving that it is exactly the application that started the login. This closes the hole through which an attacker could intercept the authorization code and exchange it for tokens instead of you. In modern versions of Keycloak, PKCE for public clients is enabled by default, so most of the time you don't need to configure anything — just know that it's working.

Confidential client

A confidential client is an application that runs on a server and can reliably keep a secret, because its code is not visible to the user: it runs on your backend, not in the browser.

Such a client has a client secret — essentially the application's own password. With it, the backend proves to Keycloak: "I really am client myapp-backend, here is my secret". This is how you configure server applications: a Spring Boot backend, a Node.js service, and so on.

The difference between the two types fits into a single table. It shows who proves their authenticity with what, and which applications each is suited for.

Client typeKeeps a secretProves authenticity withFor which applications
publicnoPKCEfrontend (SPA), mobile
confidentialyesclient secretserver backend

A simple rule worth remembering: code visible to the user — public; code only on the server — confidential.

Users

We've covered where applications live. Now — about people.

A user is a person's account inside a realm. It has a login, a password, an email, a name, and also arbitrary extra fields that Keycloak calls attributes (for example, a department or a phone number). It's the user who goes through login: they enter a login and password, and Keycloak checks them.

Where do users come from? There are several ways:

  • they are created manually in the Keycloak admin console;
  • they register themselves, if registration is enabled;
  • they are pulled in from the company's external directory (for example, LDAP or Active Directory);
  • they come from login via Google and similar external services.

An important point: a user on its own carries no permissions. "Ivan exists" is not yet "Ivan is allowed to do something". Permissions are given to a user by roles — which is what we move on to.

Roles: realm roles vs client roles

Here's the central fork of the model, which is important to understand well.

The problem: the record "user Ivan" doesn't answer the question "what is Ivan allowed to do". We need a way to say "Ivan is an administrator" or "Ivan can place orders". That's exactly what roles exist for.

A role is a label with a permission that you attach to a user. admin, manager, customer are typical roles. Then the application looks at the roles in the token and decides whether to let the user into a given part. For example, we let only those who have the admin role into the administration section.

In Keycloak roles come in two kinds, and the difference between them is a frequent source of confusion, so let's go through it in detail.

A realm role is a role common to the whole realm. It's not tied to any specific application and makes sense across the entire project at once. Good candidates for realm roles are general, cross-cutting concepts: admin, user, manager. If you have one application, or several applications but the permission means the same thing everywhere — that's a realm role.

A client role is a role that belongs to a specific client (application). It exists only in the context of that application. Why is that needed? Because the same word can mean different things in different applications. The manager role in the myapp-backend application and the manager role in the reports-app application are two independent roles, they don't overlap. A "manager" in the main application and a "manager" in the reporting system may have completely different permissions, and client roles let you keep them from getting mixed up.

An analogy that fits well: a realm role is a pass into the whole building, while a client role is a key to one specific room inside that building.

When do you choose which? For a small project, realm roles are usually enough — they're simpler: create admin and user, hand them out to users, and that's it. You reach for client roles when there are several applications and the permissions in them really differ enough that common realm roles start to get in the way.

It's also useful to know about composite roles. This is a role that includes other roles. You assign one such role to a user — and along with it they automatically get all the nested ones. The classic example: the admin role includes the user role. Then any administrator doesn't need to be given user separately — it comes along with admin. Handy for avoiding duplicate assignments.

Groups — so you don't hand out roles one by one

We know how to hand out roles to users one at a time. But imagine: you have 200 users, and each one needs to be assigned the same set of five roles. Doing this by hand, one person at a time, is agonizing and easy to get wrong somewhere (someone was forgotten a role, someone got an extra one).

Groups solve this pain.

A group is a set of users with common roles and attributes. The logic is this: you assign roles to a group once, and every user who ends up in that group automatically gets all its roles. Added a person to the group — they immediately got the needed permissions. Moved them to another group — the set of permissions changed. No need to touch each person's roles by hand.

Groups can be nested inside one another: a subgroup inherits the roles of its parent group. For example, the "Employees" group gives basic permissions, and the "Accounting" group nested in it adds its own on top — and a member of accounting gets both.

To avoid confusing roles and groups, remember the difference in one phrase: a role is the permission itself; a group is a convenient way to hand out a batch of permissions to many users at once.

Now let's put the whole model into one picture. The diagram shows a tree: the realm is the root, inside it lie clients, users, groups, and realm roles; and client roles "hang" already under a specific client, not at the realm level.

diagram

The dashed arrows in the diagram read like this: groups hand out roles to their members and contain users. The solid arrows are "what is nested inside what".

How roles get into the token

And here's a frequent point where everything breaks in practice. You assigned a role to a user in the admin console, everything looks right — but the application "doesn't see" it and won't let them in. The reason is almost always the same: the role didn't get into the token.

Recall the thought from the start of the article: a token is not a real-time query into Keycloak's database. It's a ready-made, already-signed "certificate", and the application reads permissions straight from it, without going to Keycloak. Which means the needed data must be inside the token at the moment Keycloak issues it. If the role isn't in the token — the application has no way to learn about it, no matter how many roles hang on the user in the admin console.

Two mechanisms are responsible for what gets into the token and in what form.

A protocol mapper is a rule of the form "take this (a role, an attribute, an email) and put it into the token under such-and-such a name". Good news: mappers for realm roles and for client roles exist in Keycloak out of the box and are usually enabled by default. That is, in a typical setup roles get into the token on their own, without manual work. But if someone disabled them, or you configured the client manually "from scratch" — that's where roles can get lost.

A client scope is a reusable set of mappers. Instead of attaching the same mappers to every client by hand, you gather them into a client scope and attach it to the needed clients all at once. It's simply a way not to repeat the same setup many times.

Now — where exactly roles land in the token. These fields are worth knowing by heart, because they are exactly what the application looks at:

{
  "preferred_username": "ivan",
  "realm_access": {
    "roles": ["admin", "user"]
  },
  "resource_access": {
    "myapp-backend": {
      "roles": ["manager"]
    }
  }
}

It reads like this:

  • realm roles live in the realm_access.roles field — it's a flat list of common roles;
  • client roles live in resource_access.<client-id>.roles — that is, grouped by the application they belong to.

The practical takeaway: if the user has the role but it's not in the token — don't look for the problem in the role assignment (that part is fine). Look for it in the mappers or the client scope: most likely the needed mapper is disabled or not attached.

How the application reads this (Spring Boot)

Let's tie the ends together: the token has been issued, the roles are in it — what does the server application do with it?

The server application acts as a resource server — literally a "server of resources". It doesn't handle login itself (logins, passwords, redirects) — the frontend does that together with Keycloak. The backend receives an already-issued token in the Authorization: Bearer <access_token> header, and its job is only to verify this token and let the request through or not.

The minimum configuration for Spring Boot is to specify your realm's issuer:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com/realms/myapp

Here's what happens next automatically. At this address Spring finds the service file .well-known/openid-configuration on its own (Keycloak publishes it), learns from it the address with the realm's public keys (JWKS), downloads the keys, and caches them locally. After that Spring verifies each incoming token locally: whether the signature checks out (with those very keys) and whether the issuer is the right one. No call to Keycloak happens on every request — that's why it's fast.

One typical adjustment, without which roles won't work. By default Spring doesn't know that roles live specifically in realm_access.roles — that's a Keycloak-specific format, not a general standard. So a small role converter is added: it pulls roles out of realm_access.roles and turns them into Spring's usual authorities of the form ROLE_admin. After that the usual access checks on endpoints work. This converter is covered in detail in a separate article about protecting an API — link below.

The minimum for a "backend + frontend" pairing

Now that the model is clear part by part, let's assemble it into a practical checklist. For a typical application with a server backend and a browser frontend you need:

  1. One realm for the project — for example myapp (your own, not master).
  2. Two clients in this realm:
    • myapp-frontendpublic, with PKCE, for the single-page frontend;
    • myapp-backendconfidential, with a client secret, for the server API.
  3. Roles — to start with, realm roles, for example admin and user. Client roles come in later, if there end up being several applications and the permissions start to differ.
  4. Groups — optional: handy when there are many users and the permission sets are typical.
  5. Users with assigned roles — directly or via groups.
  6. Check that the realm roles actually get into the token (the realm_access.roles field) — the mappers for this are usually already enabled, but it's worth making sure.
  7. On the backend — a resource server with issuer-uri pointing at your realm and a role converter.

That's enough for the frontend to log the user in via Keycloak, get a token and send it to the API, while the backend verifies the token and lets the user in based on roles.

In short

  • Keycloak is a separate login server: it stores users, checks passwords, issues signed tokens; applications no longer deal with passwords.
  • A realm is an isolated space (its own users, applications, roles, groups). master is only for administration; for a project you create your own realm.
  • A client is a record about an application, not about a person. Public (no secret, protected via PKCE) — for frontend and mobile; confidential (has a client secret) — for a server backend.
  • A user goes through login; permissions are carried not by the user itself but by the roles assigned to it.
  • A realm role is common to the whole realm (a pass into the whole building); a client role belongs to a specific application (a key to one room).
  • A group is a convenient way to hand out a batch of roles to many users at once.
  • Roles are put into the token by protocol mappers (often enabled by default); realm roles go into realm_access.roles, client roles into resource_access.<client-id>.roles. If a role exists but isn't in the token — the problem is in the mappers, not in the assignment.
  • Spring Boot as a resource server is configured with a single issuer-uri; it finds the keys (JWKS) itself via .well-known/openid-configuration and verifies tokens locally.
  • The minimum for backend + frontend: one realm, a public + confidential client, realm roles, users.
  • What OAuth2 and OpenID Connect are — which protocols Keycloak uses to issue tokens, and how access_token, id_token, and refresh_token differ.
  • Protecting a Spring Boot API with Keycloak — the resource server, the role converter, and access checks on endpoints in detail.