# Keycloak session limiting

This tutorial describes how to configure concurrent session limiting in Keycloak using the *Keycloak Admin Console*. After following these steps, you will be able to control how many active Keycloak sessions a user can have open at the same time.

## Prerequisites

* Keycloak **26.x** or later.
* Custody LTS Version **1.34** or later.
* Admin access to the Keycloak Admin Console.
* You are working in the **Metaco** realm.


Disruption notice
While Steps 1–6 are non-disruptive (they create or modify flows and settings that are not yet active), Steps 7–9 make changes that affect active user authentication — the sub claim value in tokens will change, and the identity provider (IDP) login flow will switch over. The steps below are ordered to minimize downtime by completing all preparatory steps first, then applying the disruptive changes (Steps 7–9) in quick succession.

For more documentation on using the Keycloak Admin Console, see Keycloak's [Server Administration Guide](https://www.keycloak.org/docs/latest/server_admin/index.html).

## Configure session limiting

The following sections detail the steps to configure concurrent session limiting in Keycloak.

For more information about using the Keycloak Admin Console to configure a realm, see [Configuring realms](https://www.keycloak.org/docs/latest/server_admin/index.html#_configuring-realms).

### Step 0: Go to the Metaco realm

Before beginning, make sure you are in the **Metaco** realm:

1. Using your administrator account, go to the URL for the Keycloak Admin Console, and log in.
2. Change the **Current realm** to **Metaco**.


### Step 1: Update realm login email settings

Update the login email settings as follows:

1. In the left menu, go to **Realm settings**.
2. Select the **Login** tab.
3. Under **Email settings**, set the following:
  * **Login with email**: **Off**
  * **Duplicate emails**: **On**


### Step 2: Create the Authentication flow

To create the Authentication flow:

1. In the left menu, go to **Authentication**.
2. Select the **Flows** tab.
3. Select **Create flow**.
4. Enter the following:
  * **Name**: **Post Broker Login with Session limit**
  * **Flow type**: **Basic flow**
5. Select **Create**.


### Step 3: Add the session limit execution

To add a new execution for setting the session limit:

1. In the newly created flow, select **Add execution**.
2. Look for **User session count limiter** (`user-session-limits`), and add it.
3. Set its requirement to **Required**.


### Step 4: Configure the session limit

1. Select the gear/settings icon next to the **User Session Limits** step.
2. Set the following values, and select **Save**.


| Setting | Field name               | Value              | Description |
|  --- | --- | --- | --- |
| Alias | `alias` | `Session limit` | Display name for this config |
| Max sessions per user (realm) | `userRealmLimit` | `1` (or your desired limit) | `0` = no limit. `1` = one session at a time. |
| Max sessions per userper client | `userClientLimit` | `0` | `0` = no limit per client. |
| Behavior | `behavior` | `Deny new session` | Defines what happens when the limit is reached. |
| Error message | `errorMessage` | `sessionLimitError` | Key used for the error page message. |


Do not modify the error message
**Do not modify** the `errorMessage` field, as it is used in the UI to show a specific descriptive error page.

Note
The `userRealmLimit` value controls how many concurrent sessions a single user can have across all clients. Set it to the number that fits your security requirements.

### Step 5: Modify the 'first broker login' flow

Modify the built-in `first broker login` flow so that it automatically links IDP users without manual confirmation or profile review.

#### Step 5a: Duplicate the 'first broker login' built-in flow

To create a modifiable copy of the `first broker login` flow:

1. In the left menu, go to **Authentication**.
2. Select the `first broker login` flow.
3. Select **Duplicate**.


#### Step 5b: Disable "Review Profile"

To prevent the user from being prompted to review or edit their profile on first login through the identity provider (IDP), which is not the desired behavior, disable the **Review Profile** step as follows:

1. Find the **Review Profile** (`idp-review-profile`) step.
2. Change its requirement from **Required** to **Disabled**.


#### Step 5c: Change "Handle Existing Account" to auto-link

To change the way the flow handles existing accounts:

1. Find the **Handle Existing Account** sub-flow.
2. Remove the existing steps inside the flow (`idp-confirm-link` and the `Account verification options` sub-flow).
3. Add a new step: **Automatically set existing user** (`idp-auto-link`).
4. Set its requirement to **Required**.


#### Step 5d: Bind the modified flow to `first broker login`

On the top right, select `action` and bind the flow to `first broker login`.

The flow should look like the following:

![](/assets/first-broker-login.3ea61a3e5cafc930e1bf68915fef524c655163225afed5eeedea6217ba46108f.bda86d28.png)

### Step 6: Configure your own IDP (skip if using default IDP)

If you are using the `Harmonize` default IDP, you can skip this step.

If you are using your own external identity provider (IDP), you need to ensure you configure the following four *mappers* for your IDP in Keycloak:

* BROKER_ID Username Template Importer
* Username with provider alias
* Hardcoded user session attribute
* BROKER_USERNAME Username Template Importer


These mappers are required for proper user linking and session tracking.

To configure the mappers:

1. In the left menu, go to **Identity Providers**.
2. Select your identity provider (IDP).
3. Select the **Mappers** tab.
4. Create these four mappers (if they don't already exist):


![](/assets/mappers-list.8a8544417d6d1467723cf9d4c5d380223d94d520e28dcd31512c521412531f72.bda86d28.png)

Configure these mappers as described in the following sections:

#### Mapper 1 — BROKER_ID Username Template Importer

This mapper maps the external user identity to the Keycloak broker ID using the IDP alias and the `sub` claim.  Configure this mapper as follows:

| Field | Value |
|  --- | --- |
| Name | `BROKER_ID Username Template Importer` |
| Mapper Type | `Username Template Importer` |
| Sync Mode | `Inherit` |
| Template | `${ALIAS}:${CLAIM.sub}` |
| Target | `BROKER_ID` |


![](/assets/add-idp-mapper-1.0ac2855b9511adc3d4a73c6a5bc386b18685c88de49b3d5f9655b44e7c376c21.bda86d28.png)

#### Mapper 2 — BROKER_USERNAME Username Template Importer

This mapper maps the external user identity to the Keycloak broker username using the same pattern. Configure this mapper as follows:

| Field | Value |
|  --- | --- |
| Name | `BROKER_USERNAME Username Template Importer` |
| Mapper Type | `Username Template Importer` |
| Sync Mode | `Inherit` |
| Template | `${ALIAS}:${CLAIM.sub}` |
| Target | `BROKER_USERNAME` |


![](/assets/add-idp-mapper-2.d68b83c60f50a20522d21b7e54f4e299e618a5f4cce2a909fcab3240ee232c9b.bda86d28.png)

#### Mapper 3 — Username with provider alias

This mapper appends the IDP alias to the username to avoid collisions between users from different identity providers. Configure this mapper as follows:

| Field | Value |
|  --- | --- |
| Name | `Username with provider alias` |
| Mapper Type | `Append provider alias to username` |
| Sync Mode | `Force` |


![](/assets/add-idp-mapper-3.e7536aa986570fb6eefea4c9b8e778bd223a1860360a6e0753a3b651e516b70d.bda86d28.png)

#### Mapper 4 — Hardcoded user session attribute

This mapper stores the IDP alias in the user session. Keycloak uses this internally to identify which provider authenticated the user. Configure this mapper as follows:

| Field | Value |
|  --- | --- |
| Name | `Hardcoded user session attribute` |
| Mapper Type | `Hardcoded User Session Attribute` |
| Attribute | `identity_provider_alias` |
| Attribute Value | (your IDP alias) |
| Sync Mode | `Force` |


![](/assets/add-idp-mapper-4.34803a611146f5bd6c6f70ded3da3a86bc5b491b4ddc9866065e922c4422fa25.bda86d28.png)

### Step 7: Update the sub claim mapper on clients

Disruption notice
From this step on, changes affect active user authentication. To minimize disruption, perform Steps 7–9 in quick succession.

With the new login flow, Keycloak no longer populates the `fullId` user attribute for users authenticating through the identity provider. You must now map the `sub` claim in JWT tokens to `username` instead.

Note
The ROPC/service client (`metaco_internal`) is not affected by this step — it does not have a **LoginId sub claim** mapper (it uses a separate **Client Credential Sub** mapper).

For each client that has a **LoginId sub claim** mapper (`metaco_desktopapp`, `metaco_webapp`, and `customer_api`):

1. Go to **Clients** and select the client.
2. Select **Client scopes > metaco_webap-dedicated > Mappers**.
3. Select **LoginId sub claim**.
4. Change **User Attribute** from `fullId` to `username`.
5. Select **Save**.


### Step 8: Update the metaco_webapp client settings

1. Go to **Clients** and select **metaco_webapp** (`${CLIENT_WEBAPP_CLIENT_ID}`).
2. Select the **Client scopes** tab:
  * Change `offline_access` from **Default** to **Optional**.
3. Select the **metaco_webap-dedicated > Scope** tab:
  * Set **Full scope allowed** to **On**.


### Step 9: Update the IDP flow overrides

This is the final cutover step. Once applied, all new identity provider (IDP) logins will use the new flows.

1. In the left menu, select **Identity Providers**.
2. Select your OpenID Connect identity provider (e.g., `Harmonize`).
3. In the **Settings** section:
  1. Find **First login flow** and change it from **Harmonize Flow** to the flow you created in Step 5.
  2. Find **Post broker login** flow and set it to: **Post Broker Login with Session limit**.
4. Select **Save**.


## Frontend client configuration

For session limiting to work correctly, the frontend application must not request the `offline_access` scope during authentication. Offline access tokens bypass the session limit because they create offline sessions that are not counted by the `user-session-limits` authenticator.

Make sure that the `OAUTH_SCOPE` (or equivalent configuration in your frontend) does not include `offline_access`.

For example, if your current scopes look like this:


```
openid offline_access
```

change them to:


```
openid
```

The reason for this is that when the `offline_access` scope is requested, Keycloak issues an offline token that persists independently of regular sessions. Since the session limit flow only counts online sessions, we want to prevent users from bypassing the limit by obtaining offline tokens.

## Verification

1. Log in as a user through the identity provider — this should work normally.
2. Open a second browser (or incognito window) and log in as the same user.
3. Verify that the second login is denied with a session limit error page (if `userRealmLimit` is set to `1`).


![](/assets/session-denied.10e3d998a882cdfa46259d1a08b4b6893672a45741c52405f14d39e663011f6f.bda86d28.png)

## Adjusting the session limit

To change the session limit after initial setup:

1. Go to **Authentication**.
2. Select **Post Broker Login with Session limit**.
3. Select the gear icon next to the "User Session Limits" step.
4. Change `userRealmLimit` to one of the following values, and select **Save**.


| Value | Behavior |
|  --- | --- |
| `0` | No limit (feature disabled) |
| `1` | One session at a time. |
| `2` | Up to 2 concurrent sessions. |
| *N* | Up to *N* concurrent sessions. |


## Troubleshooting

* **Users see a session limit error but you want to disable it:** Set `userRealmLimit` to `0` in the authenticator config (Step 4).
* **The session limit doesn't seem to apply:** Verify the identity provider's **Post broker login flow** is set to `Post Broker Login with Session limit` (Step 9).
* **Users can't log in at all:** Check that the flow requirement is set to **Required** and that the authenticator config values are correct. Setting both `userRealmLimit` and `userClientLimit` to `0` effectively disables session limiting.