How to hide or enable as read-only mode the 'username' attribute?

I need to hide or enable as read-only mode the ‘username’ attribute from the Updated profile information form [0]

We use Keycloak as an “Identity Broker” which delegates user authentication to an external “Identity Provider”.

When the external IdP user makes the first login, Keycloak shows the “Update Account Information” form [0], the ‘username’ attribute can be changed, I need to hide or enable read-only mode for the ‘username’ attribute.

I using Keycloak v20.0.5 release via docker-compose settings, I am trying adding the --spi-user-profile-declarative-user-profile-read-only-attributes=username parameter into the Keycloak command.

But it still the ‘username’ attribute can be changed from the “Update Account Information” form.

Any idea how to implement this behavior?

[0] https://domain.com/realms/my-app/login-actions/first-broker-login?client_id=myApp&tab_id=hxjfghc

Last update of this post

Edit my Dockerfile file:

...
# Array of regular expressions to identify fields that should be treated read-only so administrators can't change them.
ENV KC_SPI_USER_PROFILE_DECLARATIVE_USER_PROFILE_ADMIN_READ_ONLY_ATTRIBUTES="username"

# Array of regular expressions to identify fields that should be treated read-only so users can't change them.
ENV KC_SPI_USER_PROFILE_DECLARATIVE_USER_PROFILE_READ_ONLY_ATTRIBUTES="username"

# Enable features
ENV KC_FEATURES="docker,token-exchange,scripts,preview,admin_fine_grained_authz,declarative-user-profile"
...

Save it, and run the docker build command to generate the custom image.

Next, edit the docker-compose.yml file:

services:
...
  keycloak:
  ...
    command:
      - --verbose start --optimized
      - ...
      - --spi-user-profile-declarative-user-profile-admin-read-only-attributes="username"
      - --spi-user-profile-declarative-user-profile-read-only-attributes="username"
      - ...
  ...
...

Save it, and run the docker compose up command to create the docker containers.

So, I tried to log in with an external user, and trying to edit the Username field from the “Update Account Information” form, and the Keycloak log shows:

WARN  [org.keycloak.userprofile.validator.ReadOnlyAttributeUnchangedValidator] (executor-thread-4) Attempt to edit denied attribute '(?i:^\Qusername\E$)' of user 'new user'

This is what I have done so far!

This is my custom theme structure:

themes/
├── custom-theme
│   └── login
│       ├── login-update-profile.ftl
│       ├── resources
│       │   ├── css
│       │   │   └── custom.css
│       │   └── img
│       │       ├── favicon.ico
│       │       ├── logo.png
│       │       └── logo-text.png
│       ├── template.ftl
│       └── theme.properties
└── README.md

I added the themes/custom-theme/login/login-update-profile.ftl file with the following content:

<#import "template.ftl" as layout>
<@layout.registrationLayout; section>
    <#if section = "title">
        ${msg("loginProfileTitle")}
    <#elseif section = "header">
        ${msg("loginProfileTitle")}
    <#elseif section = "form">
        <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">

            <#-- We've disabled the 'username' field as read-only, we don't let users change it. -->
            <#if user.editUsernameAllowed>
                <div class="form-group ${messagesPerField.printIfExists('username',properties.kcFormGroupErrorClass!)}">
                    <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
                    <input type="text" id="username" name="username" class="${properties.kcInputClass!}" value="${(user.username!'')}" readonly="readonly" />
                </div>
            </#if>
            <div class="form-group ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
                <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
                <input type="text" id="email" name="email" value="${(user.email!'')}" class="${properties.kcInputClass!}" />
            </div>

            <div class="form-group ${messagesPerField.printIfExists('firstName',properties.kcFormGroupErrorClass!)}">
                <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
                <input type="text" class="${properties.kcInputClass!}" id="firstName" name="firstName" value="${(user.firstName!'')}" autocomplete="name" />
            </div>

            <div class="form-group ${messagesPerField.printIfExists('lastName',properties.kcFormGroupErrorClass!)}">
                <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
                <input type="text" class="${properties.kcInputClass!}" id="lastName" name="lastName" value="${(user.lastName!'')}" autocomplete="name" />
            </div>

            <div class="form-group">
                <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
            </div>

        </form>
    </#if>
</@layout.registrationLayout>

If only need to hide the Username field add the hidden HTML attribute into the div container for the field as the following code:

            <#if user.editUsernameAllowed>
                <div class="form-group ${messagesPerField.printIfExists('username',properties.kcFormGroupErrorClass!)} hidden">
                    <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
                    <input type="text" id="username" name="username" class="${properties.kcInputClass!}" value="${(user.username!'')}" readonly="readonly" />
                </div>
            </#if>

Save it, and reload the Keycloak instance.

So, I tried to log in with an external user, and when render the Username field is hidden from the “Update Account Information” form, as shown it in the following screenshot:

Screenshot 2024-05-30 122718 - Copy

That’s all Folks! :slight_smile:

1 Like