Skip to content

Commit

Permalink
Toggle visibility of password input fields in login-ftl-based pages
Browse files Browse the repository at this point in the history
  • Loading branch information
andreas-blaettlinger authored and pedroigor committed Sep 14, 2023
1 parent 722422a commit 86c0e33
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 92 deletions.
32 changes: 32 additions & 0 deletions docs/documentation/upgrading/topics/keycloak/changes-23_0_0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,35 @@ from the same list of values.

In this release, the role and client mappers now map to a single value from the effective roles of a user when
they are marked as single-valued (`Multivalued` disabled).

= Changes to password fields in Login UI

In this version we want to introduce a toggle to hide/show password inputs.

.Affected pages:
- login.ftl
- login-password.ftl
- login-update-password.ftl
- register.ftl
- register-user-profile.ftl

In general all `<input type="password" name="password" />` are encapsulated within a div now. The input element is followed by a button which toggles the visibility of the password input.

Old code example:
[source,html]
----
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
----

New code example:
[source,html]
----
<div class="${properties.kcInputGroup!}">
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
aria-controls="password" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>
----
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,12 @@ public void testAttributeGuiOrder() {
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(4) > div:nth-child(2) > input#password")
By.cssSelector("#password")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(5) > div:nth-child(2) > input#password-confirm")
By.cssSelector("#password-confirm")
).isDisplayed()
);
Assert.assertTrue(
Expand Down Expand Up @@ -434,12 +434,12 @@ public void testAttributeGrouping() {
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(3) > div:nth-child(2) > input#password")
By.cssSelector("#password")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#password-confirm")
By.cssSelector("#password-confirm")
).isDisplayed()
);
Assert.assertTrue(
Expand Down
24 changes: 16 additions & 8 deletions themes/src/main/resources/theme/base/login/login-password.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
<hr/>
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
type="password" autocomplete="on" autofocus
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
/>
<div class="${properties.kcInputGroup!}">
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
type="password" autocomplete="on" autofocus
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
aria-controls="password" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>
<#if messagesPerField.existsError('password')>
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('password'))?no_esc}
Expand All @@ -33,11 +40,12 @@
</div>

<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
</div>
</div>
</div>
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
</#if>

</@layout.registrationLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,31 @@
<form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<input type="text" id="username" name="username" value="${username}" autocomplete="username"
readonly="readonly" style="display:none;"/>
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
<div class="${properties.kcInputGroup!}">
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
aria-controls="password" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>

<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
autofocus autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
/>
<div class="${properties.kcInputGroup!}">
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
autofocus autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
aria-controls="password-new" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>

<#if messagesPerField.existsError('password')>
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
Expand All @@ -32,11 +46,18 @@
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password-confirm" name="password-confirm"
class="${properties.kcInputClass!}"
autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
/>
<div class="${properties.kcInputGroup!}">
<input type="password" id="password-confirm" name="password-confirm"
class="${properties.kcInputClass!}"
autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
aria-controls="password-confirm" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>

<#if messagesPerField.existsError('password-confirm')>
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
Expand All @@ -60,5 +81,6 @@
</div>
</div>
</form>
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
</#if>
</@layout.registrationLayout>
113 changes: 60 additions & 53 deletions themes/src/main/resources/theme/base/login/login.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,80 @@
<#if section = "header">
${msg("loginAccountTitle")}
<#elseif section = "form">
<div id="kc-form">
<div id="kc-form-wrapper">
<#if realm.password>
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
<#if !usernameHidden??>
<div id="kc-form">
<div id="kc-form-wrapper">
<#if realm.password>
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
<#if !usernameHidden??>
<div class="${properties.kcFormGroupClass!}">
<label for="username" class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>

<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off"
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
/>

<#if messagesPerField.existsError('username','password')>
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
</span>
</#if>

</div>
</#if>

<div class="${properties.kcFormGroupClass!}">
<label for="username" class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>

<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off"
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
/>
<div class="${properties.kcInputGroup!}">
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="off"
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
/>
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg("showPassword")}"
aria-controls="password" data-password-toggle
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
<i class="fa fa-eye" aria-hidden="true"></i>
</button>
</div>

<#if messagesPerField.existsError('username','password')>
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
</span>
</#if>

</div>
</#if>

<div class="${properties.kcFormGroupClass!}">
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>

<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="off"
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
/>

<#if usernameHidden?? && messagesPerField.existsError('username','password')>
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
</span>
</#if>

</div>

<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
<div id="kc-form-options">
<#if realm.rememberMe && !usernameHidden??>
<div class="checkbox">
<label>
<#if login.rememberMe??>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
<#else>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
</#if>
</label>
</div>
</#if>
</div>
<div class="${properties.kcFormOptionsWrapperClass!}">
<#if realm.resetPasswordAllowed>
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
<div id="kc-form-options">
<#if realm.rememberMe && !usernameHidden??>
<div class="checkbox">
<label>
<#if login.rememberMe??>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
<#else>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
</#if>
</label>
</div>
</#if>
</div>
</div>
<div class="${properties.kcFormOptionsWrapperClass!}">
<#if realm.resetPasswordAllowed>
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
</#if>
</div>

</div>
</div>

<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
</#if>
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
</#if>
</div>
</div>

</div>
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
<#elseif section = "info" >
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div id="kc-registration-container">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ password=Password
passwordConfirm=Confirm password
passwordNew=New Password
passwordNewConfirm=New Password confirmation
hidePassword=Hide password
showPassword=Show password
rememberMe=Remember me
authenticatorCode=One-time code
address=Address
Expand Down
Loading

0 comments on commit 86c0e33

Please sign in to comment.