wip: make pub.solar theme work with keycloak 25+
This commit is contained in:
parent
1202a23c20
commit
22c038dea5
|
@ -16,8 +16,8 @@ To start a Dev Keycloak instance that can show the pub.solar theme, you need to
|
||||||
-e KEYCLOAK_ADMIN_PASSWORD=admin \
|
-e KEYCLOAK_ADMIN_PASSWORD=admin \
|
||||||
-v $(pwd):/opt/keycloak/themes/pub.solar \
|
-v $(pwd):/opt/keycloak/themes/pub.solar \
|
||||||
-v $(pwd)/.dev-import:/opt/keycloak/data/import \
|
-v $(pwd)/.dev-import:/opt/keycloak/data/import \
|
||||||
quay.io/keycloak/keycloak:23.0.6 \
|
quay.io/keycloak/keycloak:25.0.6 \
|
||||||
start-dev --import-realm --features="declarative-user-profile"
|
start-dev --import-realm
|
||||||
```
|
```
|
||||||
|
|
||||||
3. After this, you can start and stop the container using `docker start keycloak-theme-dev` and `docker-stop keycloak-theme-dev`.
|
3. After this, you can start and stop the container using `docker start keycloak-theme-dev` and `docker-stop keycloak-theme-dev`.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<#if code.success>
|
<#if code.success>
|
||||||
${msg("codeSuccessTitle")}
|
${msg("codeSuccessTitle")}
|
||||||
<#else>
|
<#else>
|
||||||
${msg("codeErrorTitle", code.error)}
|
${kcSanitize(msg("codeErrorTitle", code.error))}
|
||||||
</#if>
|
</#if>
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<div id="kc-code">
|
<div id="kc-code">
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<p>${msg("copyCodeInstruction")}</p>
|
<p>${msg("copyCodeInstruction")}</p>
|
||||||
<input id="code" class="${properties.kcTextareaClass!}" value="${code.code}"/>
|
<input id="code" class="${properties.kcTextareaClass!}" value="${code.code}"/>
|
||||||
<#else>
|
<#else>
|
||||||
<p id="error">${code.error}</p>
|
<p id="error">${kcSanitize(code.error)}</p>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
15
login/delete-credential.ftl
Normal file
15
login/delete-credential.ftl
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=false; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("deleteCredentialTitle", credentialLabel)}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-delete-text">
|
||||||
|
${msg("deleteCredentialMessage", credentialLabel)}
|
||||||
|
</div>
|
||||||
|
<form class="form-actions" action="${url.loginAction}" method="POST">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="accept" id="kc-accept" type="submit" value="${msg("doConfirmDelete")}"/>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel-aia" value="${msg("doCancel")}" id="kc-decline" type="submit" />
|
||||||
|
</form>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
|
@ -2,13 +2,13 @@
|
||||||
<@layout.registrationLayout displayMessage=false; section>
|
<@layout.registrationLayout displayMessage=false; section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
<#if messageHeader??>
|
<#if messageHeader??>
|
||||||
${messageHeader}
|
${kcSanitize(msg("${messageHeader}"))?no_esc}
|
||||||
<#else>
|
<#else>
|
||||||
${message.summary}
|
${message.summary}
|
||||||
</#if>
|
</#if>
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<div id="kc-info-message">
|
<div id="kc-info-message">
|
||||||
<p class="instruction">${message.summary}<#if requiredActions??><#list requiredActions>: <b><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></b></#list><#else></#if></p>
|
<p class="instruction">${message.summary}<#if requiredActions??><#list requiredActions>: <b><#items as reqActionItem>${kcSanitize(msg("requiredAction.${reqActionItem}"))?no_esc}<#sep>, </#items></b></#list><#else></#if></p>
|
||||||
<#if skipLink??>
|
<#if skipLink??>
|
||||||
<#else>
|
<#else>
|
||||||
<#if pageRedirectUri?has_content>
|
<#if pageRedirectUri?has_content>
|
||||||
|
|
12
login/login-idp-link-confirm-override.ftl
Normal file
12
login/login-idp-link-confirm-override.ftl
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("confirmOverrideIdpTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-register-form" action="${url.loginAction}" method="post">
|
||||||
|
${msg("pageExpiredMsg1")} <a id="loginRestartLink" href="${url.loginRestartFlowUrl}">${msg("doClickHere")}</a>
|
||||||
|
|
||||||
|
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="confirmOverride" value="confirmOverride">${msg("confirmOverrideIdpContinue", idpDisplayName)}</button>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
|
@ -1,58 +1,58 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
|
||||||
<#if section="header">
|
<#if section="header">
|
||||||
${msg("doLogIn")}
|
${msg("doLogIn")}
|
||||||
<#elseif section="form">
|
<#elseif section="form">
|
||||||
<form
|
<form id="kc-otp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
|
||||||
id="kc-otp-login-form"
|
method="post">
|
||||||
class="ps-container"
|
<#if otpLogin.userOtpCredentials?size gt 1>
|
||||||
action="${url.loginAction}"
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
method="post"
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
>
|
<#list otpLogin.userOtpCredentials as otpCredential>
|
||||||
<#if otpLogin.userOtpCredentials?size gt 1>
|
<input id="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListInputClass!}" type="radio" name="selectedCredentialId" value="${otpCredential.id}" <#if otpCredential.id == otpLogin.selectedCredentialId>checked="checked"</#if>>
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<label for="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
|
||||||
<#list otpLogin.userOtpCredentials as otpCredential>
|
<span class="${properties.kcLoginOTPListItemHeaderClass!}">
|
||||||
<input id="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListInputClass!}" type="radio" name="selectedCredentialId" value="${otpCredential.id}" <#if otpCredential.id == otpLogin.selectedCredentialId>checked="checked"</#if>>
|
<span class="${properties.kcLoginOTPListItemIconBodyClass!}">
|
||||||
<label for="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
|
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
|
||||||
<span class="${properties.kcLoginOTPListItemHeaderClass!}">
|
</span>
|
||||||
<span class="${properties.kcLoginOTPListItemIconBodyClass!}">
|
<span class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</span>
|
||||||
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
|
</span>
|
||||||
</span>
|
</label>
|
||||||
<span class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</span>
|
</#list>
|
||||||
</span>
|
</div>
|
||||||
</label>
|
</div>
|
||||||
</#list>
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginOtpOneTime")}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input id="otp" name="otp" autocomplete="off" type="text" class="${properties.kcInputClass!}"
|
||||||
|
autofocus aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('totp')>
|
||||||
|
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}"
|
||||||
|
aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('totp'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginOtpOneTime")}</label>
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
id="otp"
|
<input
|
||||||
name="otp"
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
autocomplete="off"
|
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
|
||||||
type="text"
|
</div>
|
||||||
class="${properties.kcInputClass!}"
|
</div>
|
||||||
autofocus aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"
|
</form>
|
||||||
/>
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
||||||
<#if messagesPerField.existsError('totp')>
|
|
||||||
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}"
|
|
||||||
aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('totp'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<button
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
>${msg("doLogIn")}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</#if>
|
|
||||||
</@layout.registrationLayout>
|
|
143
login/login-passkeys-conditional-authenticate.ftl
Normal file
143
login/login-passkeys-conditional-authenticate.ftl
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayInfo=(realm.registrationAllowed && !registrationDisabled??); section>
|
||||||
|
<#if section = "title">
|
||||||
|
title
|
||||||
|
<#elseif section = "header">
|
||||||
|
${kcSanitize(msg("passkey-login-title"))?no_esc}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="webauth" action="${url.loginAction}" method="post">
|
||||||
|
<input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
|
||||||
|
<input type="hidden" id="authenticatorData" name="authenticatorData"/>
|
||||||
|
<input type="hidden" id="signature" name="signature"/>
|
||||||
|
<input type="hidden" id="credentialId" name="credentialId"/>
|
||||||
|
<input type="hidden" id="userHandle" name="userHandle"/>
|
||||||
|
<input type="hidden" id="error" name="error"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||||
|
<#if authenticators??>
|
||||||
|
<form id="authn_select" class="${properties.kcFormClass!}">
|
||||||
|
<#list authenticators.authenticators as authenticator>
|
||||||
|
<input type="hidden" name="authn_use_chk" value="${authenticator.credentialId}"/>
|
||||||
|
</#list>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
|
||||||
|
<#if authenticators.authenticators?size gt 1>
|
||||||
|
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("passkey-available-authenticators"))?no_esc}</p>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormClass!}">
|
||||||
|
<#list authenticators.authenticators as authenticator>
|
||||||
|
<div id="kc-webauthn-authenticator-item-${authenticator?index}" class="${properties.kcSelectAuthListItemClass!}">
|
||||||
|
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
||||||
|
<i class="${(properties['${authenticator.transports.iconClass}'])!'${properties.kcWebAuthnDefaultIcon!}'} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
||||||
|
<div id="kc-webauthn-authenticator-label-${authenticator?index}"
|
||||||
|
class="${properties.kcSelectAuthListItemHeadingClass!}">
|
||||||
|
${kcSanitize(msg('${authenticator.label}'))?no_esc}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
|
||||||
|
<div id="kc-webauthn-authenticator-transport-${authenticator?index}"
|
||||||
|
class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
<#list authenticator.transports.displayNameProperties as nameProperty>
|
||||||
|
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
|
||||||
|
<#if nameProperty?has_next>
|
||||||
|
<span>, </span>
|
||||||
|
</#if>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
<span id="kc-webauthn-authenticator-createdlabel-${authenticator?index}">
|
||||||
|
${kcSanitize(msg('passkey-createdAt-label'))?no_esc}
|
||||||
|
</span>
|
||||||
|
<span id="kc-webauthn-authenticator-created-${authenticator?index}">
|
||||||
|
${kcSanitize(authenticator.createdAt)?no_esc}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
|
||||||
|
</div>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<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" style="display:none">
|
||||||
|
<#if !usernameHidden??>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="username" class="${properties.kcLabelClass!}">${msg("passkey-autofill-select")}</label>
|
||||||
|
<input tabindex="1" id="username"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||||
|
class="${properties.kcInputClass!}" name="username"
|
||||||
|
value="${(login.username!'')}"
|
||||||
|
autocomplete="username webauthn"
|
||||||
|
type="text" autofocus autocomplete="off"/>
|
||||||
|
<#if messagesPerField.existsError('username')>
|
||||||
|
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
<div id="kc-form-passkey-button" class="${properties.kcFormButtonsClass!}" style="display:none">
|
||||||
|
<input id="authenticateWebAuthnButton" type="button" onclick="doAuthenticate([], "${rpId}", "${challenge}", ${isUserIdentified}, ${createTimeout}, "${userVerification}", "${msg("passkey-unsupported-browser-text")?no_esc}")" autofocus="autofocus"
|
||||||
|
value="${kcSanitize(msg("passkey-doAuthenticate"))}"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
|
||||||
|
</div>
|
||||||
|
<div id="kc-form-passkey-button" class="${properties.kcFormButtonsClass!}" style="display:none">
|
||||||
|
<input id="authenticateWebAuthnButton" type="button" autofocus="autofocus"
|
||||||
|
value="${kcSanitize(msg("passkey-doAuthenticate"))}"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
|
||||||
|
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
|
||||||
|
|
||||||
|
const authButton = document.getElementById('authenticateWebAuthnButton');
|
||||||
|
const input = {
|
||||||
|
isUserIdentified : ${isUserIdentified},
|
||||||
|
challenge : '${challenge}',
|
||||||
|
userVerification : '${userVerification}',
|
||||||
|
rpId : '${rpId}',
|
||||||
|
createTimeout : ${createTimeout},
|
||||||
|
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
|
||||||
|
};
|
||||||
|
authButton.addEventListener("click", () => {
|
||||||
|
authenticateByWebAuthn(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
isUserIdentified : ${isUserIdentified},
|
||||||
|
challenge : '${challenge}',
|
||||||
|
userVerification : '${userVerification}',
|
||||||
|
rpId : '${rpId}',
|
||||||
|
createTimeout : ${createTimeout},
|
||||||
|
errmsg : "${msg("passkey-unsupported-browser-text")?no_esc}"
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (event) => initAuthenticate(args));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<#elseif section = "info">
|
||||||
|
<#if realm.registrationAllowed && !registrationDisabled??>
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</@layout.registrationLayout>
|
|
@ -1,55 +1,52 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password'); section>
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password'); section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("doLogIn")}
|
${msg("doLogIn")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<form
|
<div id="kc-form">
|
||||||
id="kc-form-login"
|
<div id="kc-form-wrapper">
|
||||||
class="ps-container"
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
|
||||||
onsubmit="login.disabled = true; return true;"
|
method="post">
|
||||||
action="${url.loginAction}"
|
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||||
method="post"
|
<hr/>
|
||||||
>
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcInputGroup!}">
|
||||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
|
||||||
<input
|
type="password" autocomplete="on" autofocus
|
||||||
tabindex="2"
|
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
||||||
id="password"
|
/>
|
||||||
class="${properties.kcInputClass!}"
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg('showPassword')}"
|
||||||
name="password"
|
aria-controls="password" data-password-toggle
|
||||||
type="password"
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
autocomplete="on"
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
autofocus
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
</button>
|
||||||
/>
|
</div>
|
||||||
<#if messagesPerField.existsError('password')>
|
<#if messagesPerField.existsError('password')>
|
||||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
</span>
|
</span>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
<#if realm.resetPasswordAllowed>
|
<div id="kc-form-options">
|
||||||
<span><a tabindex="5"
|
</div>
|
||||||
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
</#if>
|
<#if realm.resetPasswordAllowed>
|
||||||
</div>
|
<span><a tabindex="5"
|
||||||
|
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
<button
|
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||||
tabindex="4"
|
</div>
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
</form>
|
||||||
name="login"
|
</div>
|
||||||
id="kc-login"
|
</div>
|
||||||
type="submit"
|
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||||
>
|
</#if>
|
||||||
${msg("doLogIn")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
${msg("recovery-code-config-header")}
|
${msg("recovery-code-config-header")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<!-- warning -->
|
<!-- warning -->
|
||||||
<div class="pf-c-alert pf-m-warning pf-m-inline ${properties.kcRecoveryCodesWarning}" aria-label="Warning alert">
|
<div class="pf-c-alert pf-m-warning pf-m-inline ${properties.kcRecoveryCodesWarning!}" aria-label="Warning alert">
|
||||||
<div class="pf-c-alert__icon">
|
<div class="pf-c-alert__icon">
|
||||||
<i class="pficon-warning-triangle-o" aria-hidden="true"></i>
|
<i class="pficon-warning-triangle-o" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
<div class="${properties.kcRecoveryCodesActions}">
|
<div class="${properties.kcRecoveryCodesActions!}">
|
||||||
<button id="printRecoveryCodes" class="pf-c-button pf-m-link" type="button">
|
<button id="printRecoveryCodes" class="pf-c-button pf-m-link" type="button">
|
||||||
<i class="pficon-print"></i> ${msg("recovery-codes-print")}
|
<i class="pficon-print"></i> ${msg("recovery-codes-print")}
|
||||||
</button>
|
</button>
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
<!-- confirmation checkbox -->
|
<!-- confirmation checkbox -->
|
||||||
<div class="${properties.kcFormOptionsClass!}">
|
<div class="${properties.kcFormOptionsClass!}">
|
||||||
<input class="${properties.kcCheckInputClass}" type="checkbox" id="kcRecoveryCodesConfirmationCheck" name="kcRecoveryCodesConfirmationCheck"
|
<input class="${properties.kcCheckInputClass!}" type="checkbox" id="kcRecoveryCodesConfirmationCheck" name="kcRecoveryCodesConfirmationCheck"
|
||||||
onchange="document.getElementById('saveRecoveryAuthnCodesBtn').disabled = !this.checked;"
|
onchange="document.getElementById('saveRecoveryAuthnCodesBtn').disabled = !this.checked;"
|
||||||
/>
|
/>
|
||||||
<label for="kcRecoveryCodesConfirmationCheck">${msg("recovery-codes-confirmation-message")}</label>
|
<label for="kcRecoveryCodesConfirmationCheck">${msg("recovery-codes-confirmation-message")}</label>
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
/* copy recovery codes */
|
/* copy recovery codes */
|
||||||
function copyRecoveryCodes() {
|
function copyRecoveryCodes() {
|
||||||
var tmpTextarea = document.createElement("textarea");
|
var tmpTextarea = document.createElement("textarea");
|
||||||
var codes = document.getElementById("kc-recovery-codes-list").getElementsByTagName("li");
|
var codes = document.querySelectorAll("#kc-recovery-codes-list li");
|
||||||
for (i = 0; i < codes.length; i++) {
|
for (i = 0; i < codes.length; i++) {
|
||||||
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\n";
|
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\n";
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRecoveryCodeList() {
|
function parseRecoveryCodeList() {
|
||||||
var recoveryCodes = document.querySelectorAll(".kc-recovery-codes-list li");
|
var recoveryCodes = document.querySelectorAll("#kc-recovery-codes-list li");
|
||||||
var recoveryCodeList = "";
|
var recoveryCodeList = "";
|
||||||
|
|
||||||
for (var i = 0; i < recoveryCodes.length; i++) {
|
for (var i = 0; i < recoveryCodes.length; i++) {
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
`@page { size: auto; margin-top: 0; }
|
`@page { size: auto; margin-top: 0; }
|
||||||
body { width: 480px; }
|
body { width: 480px; }
|
||||||
div { list-style-type: none; font-family: monospace }
|
div { list-style-type: none; font-family: monospace }
|
||||||
p:first-of-type { margin-top: 48px }`
|
p:first-of-type { margin-top: 48px }`;
|
||||||
|
|
||||||
return printFileContent =
|
return printFileContent =
|
||||||
"<html><style>" + styles + "</style><body>" +
|
"<html><style>" + styles + "</style><body>" +
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout; section>
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('recoveryCodeInput'); section>
|
||||||
|
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("auth-recovery-code-header")}
|
${msg("auth-recovery-code-header")}
|
||||||
|
@ -11,7 +11,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
<input id="recoveryCodeInput" name="recoveryCodeInput" autocomplete="off" type="text" class="${properties.kcInputClass!}" autofocus/>
|
<input tabindex="1" id="recoveryCodeInput"
|
||||||
|
name="recoveryCodeInput"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('recoveryCodeInput')>true</#if>"
|
||||||
|
autocomplete="off"
|
||||||
|
type="text"
|
||||||
|
class="${properties.kcInputClass!}"
|
||||||
|
autofocus/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('recoveryCodeInput')>
|
||||||
|
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('recoveryCodeInput'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
33
login/login-reset-otp.ftl
Normal file
33
login/login-reset-otp.ftl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
|
||||||
|
<#if section="header">
|
||||||
|
${msg("doLogIn")}
|
||||||
|
<#elseif section="form">
|
||||||
|
<form id="kc-otp-reset-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
|
||||||
|
method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<p id="kc-otp-reset-form-description">${msg("otp-reset-description")}</p>
|
||||||
|
|
||||||
|
<#list configuredOtpCredentials.userOtpCredentials as otpCredential>
|
||||||
|
<input id="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListInputClass!}" type="radio" name="selectedCredentialId" value="${otpCredential.id}" <#if otpCredential.id == configuredOtpCredentials.selectedCredentialId>checked="checked"</#if>>
|
||||||
|
<label for="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
|
||||||
|
<span class="${properties.kcLoginOTPListItemHeaderClass!}">
|
||||||
|
<span class="${properties.kcLoginOTPListItemIconBodyClass!}">
|
||||||
|
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input id="kc-otp-reset-form-submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
|
@ -1,50 +1,39 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayInfo=true displayMessage=!messagesPerField.existsError('username'); section>
|
<@layout.registrationLayout displayInfo=true displayMessage=!messagesPerField.existsError('username'); section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("emailForgotTitle")}
|
${msg("emailForgotTitle")}
|
||||||
<#elseif section = "info" >
|
<#elseif section = "form">
|
||||||
<#if realm.duplicateEmailsAllowed>
|
<form id="kc-reset-password-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
${msg("emailInstructionUsername")}
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<#else>
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
${msg("emailInstruction")}
|
<label for="username" class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
|
||||||
</#if>
|
</div>
|
||||||
<#elseif section = "form">
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
<form id="kc-reset-password-form" class="ps-container" action="${url.loginAction}" method="post">
|
<input type="text" id="username" name="username" class="${properties.kcInputClass!}" autofocus value="${(auth.attemptedUsername!'')}" aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"/>
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<#if messagesPerField.existsError('username')>
|
||||||
<label
|
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
for="username"
|
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||||
class="${properties.kcLabelClass!}"
|
</span>
|
||||||
>
|
</#if>
|
||||||
<#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if>
|
</div>
|
||||||
</label>
|
</div>
|
||||||
<input
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
type="text"
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
id="username"
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
name="username"
|
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
||||||
class="${properties.kcInputClass!}"
|
</div>
|
||||||
autofocus
|
</div>
|
||||||
value="${(auth.attemptedUsername!'')}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
|
||||||
/>
|
|
||||||
<#if messagesPerField.existsError('username')>
|
|
||||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
|
||||||
<button
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
|
||||||
>${msg("doSubmit")}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
||||||
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
<#elseif section = "info" >
|
||||||
</#if>
|
<#if realm.duplicateEmailsAllowed>
|
||||||
|
${msg("emailInstructionUsername")}
|
||||||
|
<#else>
|
||||||
|
${msg("emailInstruction")}
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
|
@ -5,19 +5,23 @@
|
||||||
${msg("updatePasswordTitle")}
|
${msg("updatePasswordTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
<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.kcFormGroupClass!}">
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
<label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
|
<label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
|
<div class="${properties.kcInputGroup!}">
|
||||||
autofocus autocomplete="new-password"
|
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
|
||||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
autofocus autocomplete="new-password"
|
||||||
/>
|
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg('showPassword')}"
|
||||||
|
aria-controls="password-new" data-password-toggle
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<#if messagesPerField.existsError('password')>
|
<#if messagesPerField.existsError('password')>
|
||||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
@ -32,11 +36,19 @@
|
||||||
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
<input type="password" id="password-confirm" name="password-confirm"
|
<div class="${properties.kcInputGroup!}">
|
||||||
class="${properties.kcInputClass!}"
|
<input type="password" id="password-confirm" name="password-confirm"
|
||||||
autocomplete="new-password"
|
class="${properties.kcInputClass!}"
|
||||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
autocomplete="new-password"
|
||||||
/>
|
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg('showPassword')}"
|
||||||
|
aria-controls="password-confirm" data-password-toggle
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<#if messagesPerField.existsError('password-confirm')>
|
<#if messagesPerField.existsError('password-confirm')>
|
||||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
@ -60,5 +72,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||||
</#if>
|
</#if>
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
|
@ -1,83 +1,12 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','email','firstName','lastName'); section>
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("loginProfileTitle")}
|
${msg("loginProfileTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
<#if user.editUsernameAllowed>
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
|
||||||
<label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
|
||||||
<input type="text" id="username" name="username" value="${(user.username!'')}"
|
|
||||||
class="${properties.kcInputClass!}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('username')>
|
<@userProfileCommons.userProfileFormFields/>
|
||||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
<#if user.editEmailAllowed>
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
|
||||||
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
|
||||||
<input type="text" id="email" name="email" value="${(user.email!'')}"
|
|
||||||
class="${properties.kcInputClass!}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('email')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('email')>
|
|
||||||
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('email'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
|
||||||
<label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
|
||||||
<input type="text" id="firstName" name="firstName" value="${(user.firstName!'')}"
|
|
||||||
class="${properties.kcInputClass!}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('firstName')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('firstName')>
|
|
||||||
<span id="input-error-firstname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('firstName'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
|
||||||
<label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
|
||||||
<input type="text" id="lastName" name="lastName" value="${(user.lastName!'')}"
|
|
||||||
class="${properties.kcInputClass!}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('lastName')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('lastName')>
|
|
||||||
<span id="input-error-lastname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('lastName'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
@ -87,13 +16,13 @@
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
<#if isAppInitiatedAction??>
|
<#if isAppInitiatedAction??>
|
||||||
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
<button class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" type="submit" name="cancel-aia" value="true" />${msg("doCancel")}</button>
|
<button class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" type="submit" name="cancel-aia" value="true" formnovalidate/>${msg("doCancel")}</button>
|
||||||
<#else>
|
<#else>
|
||||||
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</#if>
|
</#if>
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
|
@ -1,111 +1,87 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section>
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("loginAccountTitle")}
|
${msg("loginAccountTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<#if realm.password>
|
<div id="kc-form">
|
||||||
<form
|
<div id="kc-form-wrapper">
|
||||||
id="kc-form-login"
|
<#if realm.password>
|
||||||
onsubmit="login.disabled = true; return true;"
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
|
||||||
action="${url.loginAction}"
|
method="post">
|
||||||
method="post"
|
<#if !usernameHidden??>
|
||||||
class="ps-container"
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
>
|
<label for="username"
|
||||||
<#if !usernameHidden??>
|
class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
|
||||||
<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
|
<input tabindex="1" id="username"
|
||||||
tabindex="1"
|
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||||
id="username"
|
class="${properties.kcInputClass!}" name="username"
|
||||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
value="${(login.username!'')}"
|
||||||
class="${properties.kcInputClass!}"
|
type="text" autofocus autocomplete="off"/>
|
||||||
name="username"
|
|
||||||
value="${(login.username!'')}"
|
|
||||||
type="text"
|
|
||||||
autofocus
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('username')>
|
<#if messagesPerField.existsError('username')>
|
||||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||||
</span>
|
</span>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if realm.rememberMe && !usernameHidden??>
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
<div id="kc-form-options">
|
||||||
<div id="kc-form-options">
|
<#if realm.rememberMe && !usernameHidden??>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<#if login.rememberMe??>
|
<#if login.rememberMe??>
|
||||||
<input
|
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"
|
||||||
tabindex="3"
|
checked> ${msg("rememberMe")}
|
||||||
id="rememberMe"
|
<#else>
|
||||||
name="rememberMe"
|
<input tabindex="3" id="rememberMe" name="rememberMe"
|
||||||
type="checkbox"
|
type="checkbox"> ${msg("rememberMe")}
|
||||||
checked
|
</#if>
|
||||||
/> ${msg("rememberMe")}
|
</label>
|
||||||
<#else>
|
</div>
|
||||||
<input
|
</#if>
|
||||||
tabindex="3"
|
</div>
|
||||||
id="rememberMe"
|
</div>
|
||||||
name="rememberMe"
|
|
||||||
type="checkbox"
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
/> ${msg("rememberMe")}
|
<input tabindex="4"
|
||||||
</#if>
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
</label>
|
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
|
||||||
<button
|
|
||||||
tabindex="4"
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
>${msg("doLogIn")}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#elseif section = "info" >
|
<#elseif section = "info" >
|
||||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||||
<div id="kc-registration">
|
<div id="kc-registration">
|
||||||
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
<#elseif section = "socialProviders" >
|
<#elseif section = "socialProviders" >
|
||||||
<#if realm.password && social.providers??>
|
<#if realm.password && social?? && social.providers?has_content>
|
||||||
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||||
<hr/>
|
<hr/>
|
||||||
<h4>${msg("identity-provider-login-label")}</h4>
|
<h4>${msg("identity-provider-login-label")}</h4>
|
||||||
|
|
||||||
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||||
<#list social.providers as p>
|
<#list social.providers as p>
|
||||||
<a id="social-${p.alias}" class="${properties.kcFormSocialAccountListButtonClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
|
<a id="social-${p.alias}" class="${properties.kcFormSocialAccountListButtonClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
|
||||||
type="button" href="${p.loginUrl}">
|
type="button" href="${p.loginUrl}">
|
||||||
<#if p.iconClasses?has_content>
|
<#if p.iconClasses?has_content>
|
||||||
<i class="${properties.kcCommonLogoIdP!} ${p.iconClasses!}" aria-hidden="true"></i>
|
<i class="${properties.kcCommonLogoIdP!} ${p.iconClasses!}" aria-hidden="true"></i>
|
||||||
<span class="${properties.kcFormSocialAccountNameClass!} kc-social-icon-text">${p.displayName!}</span>
|
<span class="${properties.kcFormSocialAccountNameClass!} kc-social-icon-text">${p.displayName!}</span>
|
||||||
<#else>
|
<#else>
|
||||||
<span class="${properties.kcFormSocialAccountNameClass!}">${p.displayName!}</span>
|
<span class="${properties.kcFormSocialAccountNameClass!}">${p.displayName!}</span>
|
||||||
</#if>
|
</#if>
|
||||||
</a>
|
</a>
|
||||||
</#list>
|
</#list>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</#if>
|
||||||
</#if>
|
</#if>
|
||||||
</#if>
|
|
||||||
|
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
225
login/login.ftl
225
login/login.ftl
|
@ -1,132 +1,115 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section>
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("loginAccountTitle")}
|
${msg("loginAccountTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<#if realm.password>
|
<div id="kc-form">
|
||||||
<form class="ps-container" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
|
<div id="kc-form-wrapper">
|
||||||
<#if !usernameHidden??>
|
<#if realm.password>
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
|
||||||
<label
|
<#if !usernameHidden??>
|
||||||
for="username"
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
class="${properties.kcLabelClass!}"
|
<label for="username" class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
|
||||||
>
|
|
||||||
<#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
<input tabindex="2" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="username"
|
||||||
tabindex="1"
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||||
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')>
|
<#if messagesPerField.existsError('username','password')>
|
||||||
<span
|
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
id="input-error"
|
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||||
class="${properties.kcInputErrorMessageClass!}"
|
</span>
|
||||||
aria-live="polite"
|
</#if>
|
||||||
>
|
|
||||||
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
</div>
|
||||||
</span>
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputGroup!}">
|
||||||
|
<input tabindex="3" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="current-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg("showPassword")}"
|
||||||
|
aria-controls="password" data-password-toggle tabindex="4"
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#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="5" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
|
||||||
|
<#else>
|
||||||
|
<input tabindex="5" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
|
||||||
|
</#if>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<#if realm.resetPasswordAllowed>
|
||||||
|
<span><a tabindex="6" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||||
|
</#if>
|
||||||
|
</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="7" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</#if>
|
</#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">
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>${msg("noAccount")} <a tabindex="8"
|
||||||
|
href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
|
<#elseif section = "socialProviders" >
|
||||||
|
<#if realm.password && social?? && social.providers?has_content>
|
||||||
|
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||||
|
<hr/>
|
||||||
|
<h2>${msg("identity-provider-login-label")}</h2>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
<#list social.providers as p>
|
||||||
|
<li>
|
||||||
<input
|
<a id="social-${p.alias}" class="${properties.kcFormSocialAccountListButtonClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
|
||||||
tabindex="2"
|
type="button" href="${p.loginUrl}">
|
||||||
id="password"
|
<#if p.iconClasses?has_content>
|
||||||
class="${properties.kcInputClass!}"
|
<i class="${properties.kcCommonLogoIdP!} ${p.iconClasses!}" aria-hidden="true"></i>
|
||||||
name="password"
|
<span class="${properties.kcFormSocialAccountNameClass!} kc-social-icon-text">${p.displayName!}</span>
|
||||||
type="password"
|
<#else>
|
||||||
autocomplete="off"
|
<span class="${properties.kcFormSocialAccountNameClass!}">${p.displayName!}</span>
|
||||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
</#if>
|
||||||
/>
|
</a>
|
||||||
|
</li>
|
||||||
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
</#list>
|
||||||
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
</ul>
|
||||||
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#if realm.resetPasswordAllowed>
|
|
||||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
|
||||||
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
|
||||||
<#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 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>/>
|
|
||||||
<button
|
|
||||||
tabindex="4"
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
>${msg("doLogIn")}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#elseif section = "info" >
|
|
||||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
|
||||||
<div id="kc-registration-container">
|
|
||||||
<div id="kc-registration">
|
|
||||||
<span>${msg("noAccount")}
|
|
||||||
<a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
<#elseif section = "socialProviders" >
|
|
||||||
<#if realm.password && social.providers??>
|
|
||||||
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
|
||||||
<hr/>
|
|
||||||
<h4>${msg("identity-provider-login-label")}</h4>
|
|
||||||
|
|
||||||
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
|
||||||
<#list social.providers as p>
|
|
||||||
<a
|
|
||||||
id="social-${p.alias}"
|
|
||||||
class="${properties.kcFormSocialAccountListButtonClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
|
|
||||||
type="button"
|
|
||||||
href="${p.loginUrl}"
|
|
||||||
>
|
|
||||||
<#if p.iconClasses?has_content>
|
|
||||||
<i class="${properties.kcCommonLogoIdP!} ${p.iconClasses!}" aria-hidden="true"></i>
|
|
||||||
<span class="${properties.kcFormSocialAccountNameClass!} kc-social-icon-text">${p.displayName!}</span>
|
|
||||||
<#else>
|
|
||||||
<span class="${properties.kcFormSocialAccountNameClass!}">${p.displayName!}</span>
|
|
||||||
</#if>
|
|
||||||
</a>
|
|
||||||
</#list>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
27
login/register-commons.ftl
Normal file
27
login/register-commons.ftl
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<#macro termsAcceptance>
|
||||||
|
<#if termsAcceptanceRequired??>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
${msg("termsTitle")}
|
||||||
|
<div id="kc-registration-terms-text">
|
||||||
|
${kcSanitize(msg("termsText"))?no_esc}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<input type="checkbox" id="termsAccepted" name="termsAccepted" class="${properties.kcCheckboxInputClass!}"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('termsAccepted')>true</#if>"
|
||||||
|
/>
|
||||||
|
<label for="termsAccepted" class="${properties.kcLabelClass!}">${msg("acceptTerms")}</label>
|
||||||
|
</div>
|
||||||
|
<#if messagesPerField.existsError('termsAccepted')>
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<span id="input-error-terms-accepted" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('termsAccepted'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#macro>
|
|
@ -1,112 +1,112 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('firstName','lastName','email','username','password','password-confirm'); section>
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
<#if section = "header">
|
<#import "register-commons.ftl" as registerCommons>
|
||||||
${msg("registerTitle")}
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
<#elseif section = "form">
|
<#if section = "header">
|
||||||
<form id="kc-register-form" class="ps-container" action="${url.registrationAction}" method="post">
|
<#if messageHeader??>
|
||||||
<!--div class="${properties.kcFormGroupClass!}">
|
${kcSanitize(msg("${messageHeader}"))?no_esc}
|
||||||
<label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
|
<#else>
|
||||||
<input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName"
|
${msg("registerTitle")}
|
||||||
value="${(register.formData.firstName!'')}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('firstName')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('firstName')>
|
|
||||||
<span id="input-error-firstname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('firstName'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
<#elseif section = "form">
|
||||||
|
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<@userProfileCommons.userProfileFormFields; callback, attribute>
|
||||||
<label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
|
<#if callback = "afterField">
|
||||||
<input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName"
|
<#-- render password fields just under the username or email (if used as username) -->
|
||||||
value="${(register.formData.lastName!'')}"
|
<#if passwordRequired?? && (attribute.name == 'username' || (attribute.name == 'email' && realm.registrationEmailAsUsername))>
|
||||||
aria-invalid="<#if messagesPerField.existsError('lastName')>true</#if>"
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
/>
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label> *
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<div class="${properties.kcInputGroup!}">
|
||||||
|
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg('showPassword')}"
|
||||||
|
aria-controls="password" data-password-toggle
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<#if messagesPerField.existsError('lastName')>
|
<#if messagesPerField.existsError('password')>
|
||||||
<span id="input-error-lastname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
${kcSanitize(messagesPerField.get('lastName'))?no_esc}
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
</span>
|
</span>
|
||||||
</#if>
|
</#if>
|
||||||
</div-->
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<#if !realm.registrationEmailAsUsername>
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
<label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
|
<label for="password-confirm"
|
||||||
<input type="text" id="username" class="${properties.kcInputClass!}" name="username"
|
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label> *
|
||||||
value="${(register.formData.username!'')}" autocomplete="username"
|
</div>
|
||||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
/>
|
<div class="${properties.kcInputGroup!}">
|
||||||
|
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||||
|
name="password-confirm"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg('showPassword')}"
|
||||||
|
aria-controls="password-confirm" data-password-toggle
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<#if messagesPerField.existsError('username')>
|
<#if messagesPerField.existsError('password-confirm')>
|
||||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
||||||
</span>
|
</span>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</@userProfileCommons.userProfileFormFields>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<@registerCommons.termsAcceptance/>
|
||||||
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
|
|
||||||
<input type="text" id="email" class="${properties.kcInputClass!}" name="email"
|
|
||||||
value="${(register.formData.email!'')}" autocomplete="email"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('email')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('email')>
|
<#if recaptchaRequired?? && (recaptchaVisible!false)>
|
||||||
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<div class="form-group">
|
||||||
${kcSanitize(messagesPerField.get('email'))?no_esc}
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
</span>
|
<div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}" data-action="${recaptchaAction}"></div>
|
||||||
</#if>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
<#if passwordRequired??>
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
||||||
autocomplete="new-password"
|
</div>
|
||||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
</div>
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('password')>
|
<#if recaptchaRequired?? && !(recaptchaVisible!false)>
|
||||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
<script>
|
||||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
function onSubmitRecaptcha(token) {
|
||||||
</span>
|
document.getElementById("kc-register-form").submit();
|
||||||
</#if>
|
}
|
||||||
</div>
|
</script>
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<button class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!} g-recaptcha"
|
||||||
<label for="password-confirm"
|
data-sitekey="${recaptchaSiteKey}" data-callback='onSubmitRecaptcha' data-action='${recaptchaAction}' type="submit">
|
||||||
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
${msg("doRegister")}
|
||||||
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
</button>
|
||||||
name="password-confirm"
|
</div>
|
||||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
<#else>
|
||||||
/>
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
|
||||||
<#if messagesPerField.existsError('password-confirm')>
|
</div>
|
||||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
</#if>
|
||||||
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
</div>
|
||||||
</span>
|
</form>
|
||||||
</#if>
|
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||||
</div>
|
</#if>
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#if recaptchaRequired??>
|
|
||||||
<div class="ps-form-group">
|
|
||||||
<div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<button
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
|
||||||
>${msg("doRegister")}</button>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
|
||||||
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</#if>
|
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout displayInfo=false; section>
|
<@layout.registrationLayout displayInfo=false; section>
|
||||||
<#if section = "header" || section = "show-username">
|
<#if section = "header" || section = "show-username">
|
||||||
<script type="text/javascript">
|
|
||||||
function fillAndSubmit(authExecId) {
|
|
||||||
document.getElementById('authexec-hidden-input').value = authExecId;
|
|
||||||
document.getElementById('kc-select-credential-form').submit();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("loginChooseAuthenticator")}
|
${msg("loginChooseAuthenticator")}
|
||||||
</#if>
|
</#if>
|
||||||
|
@ -15,13 +9,13 @@
|
||||||
<form id="kc-select-credential-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
<form id="kc-select-credential-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
<div class="${properties.kcSelectAuthListClass!}">
|
<div class="${properties.kcSelectAuthListClass!}">
|
||||||
<#list auth.authenticationSelections as authenticationSelection>
|
<#list auth.authenticationSelections as authenticationSelection>
|
||||||
<div class="${properties.kcSelectAuthListItemClass!}" onclick="fillAndSubmit('${authenticationSelection.authExecId}')">
|
<button class="${properties.kcSelectAuthListItemClass!}" type="submit" name="authenticationExecution" value="${authenticationSelection.authExecId}">
|
||||||
|
|
||||||
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
||||||
<i class="${properties['${authenticationSelection.iconCssClass}']!authenticationSelection.iconCssClass} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
<i class="${properties['${authenticationSelection.iconCssClass}']!authenticationSelection.iconCssClass} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
||||||
<div class="${properties.kcSelectAuthListItemHeadingClass!} ${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}">
|
<div class="${properties.kcSelectAuthListItemHeadingClass!}">
|
||||||
${msg('${authenticationSelection.displayName}')}
|
${msg('${authenticationSelection.displayName}')}
|
||||||
</div>
|
</div>
|
||||||
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
@ -32,9 +26,8 @@
|
||||||
<div class="${properties.kcSelectAuthListItemArrowClass!}">
|
<div class="${properties.kcSelectAuthListItemArrowClass!}">
|
||||||
<i class="${properties.kcSelectAuthListItemArrowIconClass!}"></i>
|
<i class="${properties.kcSelectAuthListItemArrowIconClass!}"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</#list>
|
</#list>
|
||||||
<input type="hidden" id="authexec-hidden-input" name="authenticationExecution" />
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,62 @@
|
||||||
<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false>
|
<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false>
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html class="${properties.kcHtmlClass!}"<#if realm.internationalizationEnabled> lang="${locale.currentLanguageTag}"</#if>>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<meta name="robots" content="noindex, nofollow">
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<#if properties.meta?has_content>
|
||||||
|
<#list properties.meta?split(' ') as meta>
|
||||||
|
<meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}"/>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
|
||||||
<title>${msg("loginTitle",(realm.displayName!''))}</title>
|
<title>${msg("loginTitle",(realm.displayName!''))}</title>
|
||||||
|
|
||||||
<link rel="icon" href="${url.resourcesPath}/img/pub.solar.svg" />
|
<link rel="icon" href="${url.resourcesPath}/img/pub.solar.svg" />
|
||||||
|
|
||||||
<link href="${url.resourcesPath}/css/index.css?v4" rel="stylesheet" />
|
<link href="${url.resourcesPath}/css/index.css?v4" rel="stylesheet" />
|
||||||
|
|
||||||
<script defer src="${url.resourcesPath}/js/background.js" type="text/javascript"></script>
|
<script defer src="${url.resourcesPath}/js/background.js" type="text/javascript"></script>
|
||||||
<script defer src="${url.resourcesPath}/js/i18n.js" type="text/javascript"></script>
|
<script defer src="${url.resourcesPath}/js/i18n.js" type="text/javascript"></script>
|
||||||
|
|
||||||
<#if properties.stylesCommon?has_content>
|
<#if properties.stylesCommon?has_content>
|
||||||
<#list properties.stylesCommon?split(' ') as style>
|
<#list properties.stylesCommon?split(' ') as style>
|
||||||
<link href="${url.resourcesCommonPath}/${style}" rel="stylesheet" />
|
<link href="${url.resourcesCommonPath}/${style}" rel="stylesheet" />
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
</#if>
|
||||||
<#if properties.styles?has_content>
|
<#if properties.styles?has_content>
|
||||||
<#list properties.styles?split(' ') as style>
|
<#list properties.styles?split(' ') as style>
|
||||||
<link href="${url.resourcesPath}/${style}" rel="stylesheet" />
|
<link href="${url.resourcesPath}/${style}" rel="stylesheet" />
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
</#if>
|
||||||
<#if properties.scripts?has_content>
|
<#if properties.scripts?has_content>
|
||||||
<#list properties.scripts?split(' ') as script>
|
<#list properties.scripts?split(' ') as script>
|
||||||
<script defer src="${url.resourcesPath}/${script}" type="text/javascript"></script>
|
<script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
</#if>
|
||||||
<#if properties.scriptsCommon?has_content>
|
<script type="importmap">
|
||||||
<#list properties.scriptsCommon?split(' ') as script>
|
{
|
||||||
<script defer src="${url.resourcesCommonPath}/${script}" type="text/javascript"></script>
|
"imports": {
|
||||||
</#list>
|
"rfc4648": "${url.resourcesCommonPath}/node_modules/rfc4648/lib/rfc4648.js"
|
||||||
</#if>
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="${url.resourcesPath}/js/menu-button-links.js" type="module"></script>
|
||||||
|
<#if scripts??>
|
||||||
|
<#list scripts as script>
|
||||||
|
<script src="${script}" type="text/javascript"></script>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<script type="module">
|
||||||
|
import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";
|
||||||
|
|
||||||
|
checkCookiesAndSetTimer(
|
||||||
|
"${url.ssoLoginInOtherTabsUrl?no_esc}"
|
||||||
|
);
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="ps-main ps-main_full">
|
<body class="ps-main ps-main_full">
|
||||||
|
|
|
@ -16,3 +16,10 @@ kcInputErrorMessageClass=ps-form-group--error
|
||||||
kcInputClass=ps-input
|
kcInputClass=ps-input
|
||||||
|
|
||||||
kcWebAuthnKeyIcon=pficon pficon-key
|
kcWebAuthnKeyIcon=pficon pficon-key
|
||||||
|
|
||||||
|
kcAuthenticatorPasswordClass=ps-button
|
||||||
|
kcAuthenticatorWebAuthnClass=ps-button
|
||||||
|
kcAuthenticatorWebAuthnPasswordlessClass=ps-button
|
||||||
|
|
||||||
|
kcFormPasswordVisibilityIconShow=fa fa-eye
|
||||||
|
kcFormPasswordVisibilityIconHide=fa fa-eye-slash
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<#import "password-commons.ftl" as passwordCommons>
|
<#import "password-commons.ftl" as passwordCommons>
|
||||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('email'); section>
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
<#if section = "header">
|
<#if section = "header">
|
||||||
${msg("updateEmailTitle")}
|
${msg("updateEmailTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<form id="kc-update-email-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
<form id="kc-update-email-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<@userProfileCommons.userProfileFormFields/>
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
|
||||||
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
|
||||||
<input type="text" id="email" name="email" value="${(email.value!'')}"
|
|
||||||
class="${properties.kcInputClass!}"
|
|
||||||
aria-invalid="<#if messagesPerField.existsError('email')>true</#if>"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<#if messagesPerField.existsError('email')>
|
|
||||||
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
|
||||||
${kcSanitize(messagesPerField.get('email'))?no_esc}
|
|
||||||
</span>
|
|
||||||
</#if>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
|
|
@ -3,27 +3,31 @@
|
||||||
|
|
||||||
<#list profile.attributes as attribute>
|
<#list profile.attributes as attribute>
|
||||||
|
|
||||||
<#assign groupName = attribute.group!"">
|
<#assign group = (attribute.group)!"">
|
||||||
<#if groupName != currentGroup>
|
<#if group != currentGroup>
|
||||||
<#assign currentGroup=groupName>
|
<#assign currentGroup=group>
|
||||||
<#if currentGroup != "" >
|
<#if currentGroup != "">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<div class="${properties.kcFormGroupClass!}"
|
||||||
|
<#list group.html5DataAnnotations as key, value>
|
||||||
|
data-${key}="${value}"
|
||||||
|
</#list>
|
||||||
|
>
|
||||||
|
|
||||||
<#assign groupDisplayHeader=attribute.groupDisplayHeader!"">
|
<#assign groupDisplayHeader=group.displayHeader!"">
|
||||||
<#if groupDisplayHeader != "">
|
<#if groupDisplayHeader != "">
|
||||||
<#assign groupHeaderText=advancedMsg(attribute.groupDisplayHeader)!groupName>
|
<#assign groupHeaderText=advancedMsg(groupDisplayHeader)!group>
|
||||||
<#else>
|
<#else>
|
||||||
<#assign groupHeaderText=groupName>
|
<#assign groupHeaderText=group.name!"">
|
||||||
</#if>
|
</#if>
|
||||||
<div class="${properties.kcContentWrapperClass!}">
|
<div class="${properties.kcContentWrapperClass!}">
|
||||||
<label id="header-${groupName}" class="${kcFormGroupHeader!}">${groupHeaderText}</label>
|
<label id="header-${attribute.group.name}" class="${kcFormGroupHeader!}">${groupHeaderText}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<#assign groupDisplayDescription=attribute.groupDisplayDescription!"">
|
<#assign groupDisplayDescription=group.displayDescription!"">
|
||||||
<#if groupDisplayDescription != "">
|
<#if groupDisplayDescription != "">
|
||||||
<#assign groupDescriptionText=advancedMsg(attribute.groupDisplayDescription)!"">
|
<#assign groupDescriptionText=advancedMsg(groupDisplayDescription)!"">
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
<label id="description-${groupName}" class="${properties.kcLabelClass!}">${groupDescriptionText}</label>
|
<label id="description-${group.name}" class="${properties.kcLabelClass!}">${groupDescriptionText}</label>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +57,10 @@
|
||||||
</div>
|
</div>
|
||||||
<#nested "afterField" attribute>
|
<#nested "afterField" attribute>
|
||||||
</#list>
|
</#list>
|
||||||
|
|
||||||
|
<#list profile.html5DataAnnotations?keys as key>
|
||||||
|
<script type="module" src="${url.resourcesPath}/js/${key}.js"></script>
|
||||||
|
</#list>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
<#macro inputFieldByType attribute>
|
<#macro inputFieldByType attribute>
|
||||||
|
@ -69,16 +77,22 @@
|
||||||
<@inputTagSelects attribute=attribute/>
|
<@inputTagSelects attribute=attribute/>
|
||||||
<#break>
|
<#break>
|
||||||
<#default>
|
<#default>
|
||||||
<@inputTag attribute=attribute/>
|
<#if attribute.multivalued && attribute.values?has_content>
|
||||||
|
<#list attribute.values as value>
|
||||||
|
<@inputTag attribute=attribute value=value!''/>
|
||||||
|
</#list>
|
||||||
|
<#else>
|
||||||
|
<@inputTag attribute=attribute value=attribute.value!''/>
|
||||||
|
</#if>
|
||||||
</#switch>
|
</#switch>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
<#macro inputTag attribute>
|
<#macro inputTag attribute value>
|
||||||
<input type="<@inputTagType attribute=attribute/>" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}" class="${properties.kcInputClass!}"
|
<input type="<@inputTagType attribute=attribute/>" id="${attribute.name}" name="${attribute.name}" value="${(value!'')}" class="${properties.kcInputClass!}"
|
||||||
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
<#if attribute.readOnly>disabled</#if>
|
<#if attribute.readOnly>disabled</#if>
|
||||||
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
|
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
|
||||||
<#if attribute.annotations.inputTypePlaceholder??>placeholder="${attribute.annotations.inputTypePlaceholder}"</#if>
|
<#if attribute.annotations.inputTypePlaceholder??>placeholder="${advancedMsg(attribute.annotations.inputTypePlaceholder)}"</#if>
|
||||||
<#if attribute.annotations.inputTypePattern??>pattern="${attribute.annotations.inputTypePattern}"</#if>
|
<#if attribute.annotations.inputTypePattern??>pattern="${attribute.annotations.inputTypePattern}"</#if>
|
||||||
<#if attribute.annotations.inputTypeSize??>size="${attribute.annotations.inputTypeSize}"</#if>
|
<#if attribute.annotations.inputTypeSize??>size="${attribute.annotations.inputTypeSize}"</#if>
|
||||||
<#if attribute.annotations.inputTypeMaxlength??>maxlength="${attribute.annotations.inputTypeMaxlength}"</#if>
|
<#if attribute.annotations.inputTypeMaxlength??>maxlength="${attribute.annotations.inputTypeMaxlength}"</#if>
|
||||||
|
@ -86,6 +100,10 @@
|
||||||
<#if attribute.annotations.inputTypeMax??>max="${attribute.annotations.inputTypeMax}"</#if>
|
<#if attribute.annotations.inputTypeMax??>max="${attribute.annotations.inputTypeMax}"</#if>
|
||||||
<#if attribute.annotations.inputTypeMin??>min="${attribute.annotations.inputTypeMin}"</#if>
|
<#if attribute.annotations.inputTypeMin??>min="${attribute.annotations.inputTypeMin}"</#if>
|
||||||
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
|
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
|
||||||
|
<#list attribute.html5DataAnnotations as key, value>
|
||||||
|
data-${key}="${value}"
|
||||||
|
</#list>
|
||||||
/>
|
/>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
@ -128,13 +146,14 @@
|
||||||
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
||||||
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
||||||
<#assign options=attribute.validators.options.options>
|
<#assign options=attribute.validators.options.options>
|
||||||
|
<#else>
|
||||||
|
<#assign options=[]>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if options??>
|
<#list options as option>
|
||||||
<#list options as option>
|
|
||||||
<option value="${option}" <#if attribute.values?seq_contains(option)>selected</#if>><@selectOptionLabelText attribute=attribute option=option/></option>
|
<option value="${option}" <#if attribute.values?seq_contains(option)>selected</#if>><@selectOptionLabelText attribute=attribute option=option/></option>
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
|
||||||
</select>
|
</select>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
@ -144,21 +163,22 @@
|
||||||
<#assign classDiv=properties.kcInputClassRadio!>
|
<#assign classDiv=properties.kcInputClassRadio!>
|
||||||
<#assign classInput=properties.kcInputClassRadioInput!>
|
<#assign classInput=properties.kcInputClassRadioInput!>
|
||||||
<#assign classLabel=properties.kcInputClassRadioLabel!>
|
<#assign classLabel=properties.kcInputClassRadioLabel!>
|
||||||
<#else>
|
<#else>
|
||||||
<#assign inputType='checkbox'>
|
<#assign inputType='checkbox'>
|
||||||
<#assign classDiv=properties.kcInputClassCheckbox!>
|
<#assign classDiv=properties.kcInputClassCheckbox!>
|
||||||
<#assign classInput=properties.kcInputClassCheckboxInput!>
|
<#assign classInput=properties.kcInputClassCheckboxInput!>
|
||||||
<#assign classLabel=properties.kcInputClassCheckboxLabel!>
|
<#assign classLabel=properties.kcInputClassCheckboxLabel!>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??>
|
<#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??>
|
||||||
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
||||||
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
||||||
<#assign options=attribute.validators.options.options>
|
<#assign options=attribute.validators.options.options>
|
||||||
|
<#else>
|
||||||
|
<#assign options=[]>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if options??>
|
<#list options as option>
|
||||||
<#list options as option>
|
|
||||||
<div class="${classDiv}">
|
<div class="${classDiv}">
|
||||||
<input type="${inputType}" id="${attribute.name}-${option}" name="${attribute.name}" value="${option}" class="${classInput}"
|
<input type="${inputType}" id="${attribute.name}-${option}" name="${attribute.name}" value="${option}" class="${classInput}"
|
||||||
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
|
@ -167,9 +187,7 @@
|
||||||
/>
|
/>
|
||||||
<label for="${attribute.name}-${option}" class="${classLabel}<#if attribute.readOnly> ${properties.kcInputClassRadioCheckboxLabelDisabled!}</#if>"><@selectOptionLabelText attribute=attribute option=option/></label>
|
<label for="${attribute.name}-${option}" class="${classLabel}<#if attribute.readOnly> ${properties.kcInputClassRadioCheckboxLabelDisabled!}</#if>"><@selectOptionLabelText attribute=attribute option=option/></label>
|
||||||
</div>
|
</div>
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
|
||||||
</select>
|
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
<#macro selectOptionLabelText attribute option>
|
<#macro selectOptionLabelText attribute option>
|
||||||
|
@ -184,4 +202,4 @@
|
||||||
</#if>
|
</#if>
|
||||||
</#if>
|
</#if>
|
||||||
</#compress>
|
</#compress>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
|
@ -1,168 +1,102 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<@layout.registrationLayout; section>
|
<@layout.registrationLayout displayInfo=(realm.registrationAllowed && !registrationDisabled??); section>
|
||||||
<#if section = "title">
|
<#if section = "title">
|
||||||
title
|
title
|
||||||
<#elseif section = "header">
|
<#elseif section = "header">
|
||||||
${kcSanitize(msg("webauthn-login-title"))?no_esc}
|
${kcSanitize(msg("webauthn-login-title"))?no_esc}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<div id="kc-form-webauthn" class="${properties.kcFormClass!}">
|
<div id="kc-form-webauthn" class="${properties.kcFormClass!}">
|
||||||
<form id="webauth" action="${url.loginAction}" method="post">
|
<form id="webauth" action="${url.loginAction}" method="post">
|
||||||
<input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
|
<input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
|
||||||
<input type="hidden" id="authenticatorData" name="authenticatorData"/>
|
<input type="hidden" id="authenticatorData" name="authenticatorData"/>
|
||||||
<input type="hidden" id="signature" name="signature"/>
|
<input type="hidden" id="signature" name="signature"/>
|
||||||
<input type="hidden" id="credentialId" name="credentialId"/>
|
<input type="hidden" id="credentialId" name="credentialId"/>
|
||||||
<input type="hidden" id="userHandle" name="userHandle"/>
|
<input type="hidden" id="userHandle" name="userHandle"/>
|
||||||
<input type="hidden" id="error" name="error"/>
|
<input type="hidden" id="error" name="error"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||||
<#if authenticators??>
|
<#if authenticators??>
|
||||||
<form id="authn_select" class="${properties.kcFormClass!}">
|
<form id="authn_select" class="${properties.kcFormClass!}">
|
||||||
<#list authenticators.authenticators as authenticator>
|
<#list authenticators.authenticators as authenticator>
|
||||||
<input type="hidden" name="authn_use_chk" value="${authenticator.credentialId}"/>
|
<input type="hidden" name="authn_use_chk" value="${authenticator.credentialId}"/>
|
||||||
</#list>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
|
|
||||||
<#if authenticators.authenticators?size gt 1>
|
|
||||||
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}</p>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div class="${properties.kcFormClass!}">
|
|
||||||
<#list authenticators.authenticators as authenticator>
|
|
||||||
<div id="kc-webauthn-authenticator" class="${properties.kcSelectAuthListItemClass!}">
|
|
||||||
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
|
||||||
<i class="${(properties['${authenticator.transports.iconClass}'])!'${properties.kcWebAuthnDefaultIcon!}'} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
|
||||||
<div id="kc-webauthn-authenticator-label"
|
|
||||||
class="${properties.kcSelectAuthListItemHeadingClass!}">
|
|
||||||
${kcSanitize(msg('${authenticator.label}'))?no_esc}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
|
|
||||||
<div id="kc-webauthn-authenticator-transport"
|
|
||||||
class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
|
||||||
<#list authenticator.transports.displayNameProperties as nameProperty>
|
|
||||||
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
|
|
||||||
<#if nameProperty?has_next>
|
|
||||||
<span>, </span>
|
|
||||||
</#if>
|
|
||||||
</#list>
|
</#list>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
|
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
|
||||||
|
<#if authenticators.authenticators?size gt 1>
|
||||||
|
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}</p>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormClass!}">
|
||||||
|
<#list authenticators.authenticators as authenticator>
|
||||||
|
<div id="kc-webauthn-authenticator-item-${authenticator?index}" class="${properties.kcSelectAuthListItemClass!}">
|
||||||
|
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
||||||
|
<i class="${(properties['${authenticator.transports.iconClass}'])!'${properties.kcWebAuthnDefaultIcon!}'} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
||||||
|
<div id="kc-webauthn-authenticator-label-${authenticator?index}"
|
||||||
|
class="${properties.kcSelectAuthListItemHeadingClass!}">
|
||||||
|
${kcSanitize(msg('${authenticator.label}'))?no_esc}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
|
||||||
|
<div id="kc-webauthn-authenticator-transport-${authenticator?index}"
|
||||||
|
class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
<#list authenticator.transports.displayNameProperties as nameProperty>
|
||||||
|
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
|
||||||
|
<#if nameProperty?has_next>
|
||||||
|
<span>, </span>
|
||||||
|
</#if>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
<span id="kc-webauthn-authenticator-createdlabel-${authenticator?index}">
|
||||||
|
${kcSanitize(msg('webauthn-createdAt-label'))?no_esc}
|
||||||
|
</span>
|
||||||
|
<span id="kc-webauthn-authenticator-created-${authenticator?index}">
|
||||||
|
${kcSanitize(authenticator.createdAt)?no_esc}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
|
||||||
|
</div>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
||||||
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
<span id="kc-webauthn-authenticator-created-label">
|
<input id="authenticateWebAuthnButton" type="button" autofocus="autofocus"
|
||||||
${kcSanitize(msg('webauthn-createdAt-label'))?no_esc}
|
value="${kcSanitize(msg("webauthn-doAuthenticate"))}"
|
||||||
</span>
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
|
||||||
<span id="kc-webauthn-authenticator-created">
|
|
||||||
${kcSanitize(authenticator.createdAt)?no_esc}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</#list>
|
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
|
||||||
<input id="authenticateWebAuthnButton" type="button" onclick="webAuthnAuthenticate()" autofocus="autofocus"
|
|
||||||
value="${kcSanitize(msg("webauthn-doAuthenticate"))}"
|
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js"></script>
|
<script type="module">
|
||||||
<script type="text/javascript" src="${url.resourcesPath}/js/base64url.js"></script>
|
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
|
||||||
<script type="text/javascript">
|
const authButton = document.getElementById('authenticateWebAuthnButton');
|
||||||
function webAuthnAuthenticate() {
|
authButton.addEventListener("click", function() {
|
||||||
let isUserIdentified = ${isUserIdentified};
|
const input = {
|
||||||
if (!isUserIdentified) {
|
isUserIdentified : ${isUserIdentified},
|
||||||
doAuthenticate([]);
|
challenge : '${challenge}',
|
||||||
return;
|
userVerification : '${userVerification}',
|
||||||
}
|
rpId : '${rpId}',
|
||||||
checkAllowCredentials();
|
createTimeout : ${createTimeout},
|
||||||
}
|
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
|
||||||
|
};
|
||||||
|
authenticateByWebAuthn(input);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
function checkAllowCredentials() {
|
<#elseif section = "info">
|
||||||
let allowCredentials = [];
|
<#if realm.registrationAllowed && !registrationDisabled??>
|
||||||
let authn_use = document.forms['authn_select'].authn_use_chk;
|
<div id="kc-registration">
|
||||||
|
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
if (authn_use !== undefined) {
|
</div>
|
||||||
if (authn_use.length === undefined) {
|
</#if>
|
||||||
allowCredentials.push({
|
</#if>
|
||||||
id: base64url.decode(authn_use.value, {loose: true}),
|
</@layout.registrationLayout>
|
||||||
type: 'public-key',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < authn_use.length; i++) {
|
|
||||||
allowCredentials.push({
|
|
||||||
id: base64url.decode(authn_use[i].value, {loose: true}),
|
|
||||||
type: 'public-key',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doAuthenticate(allowCredentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function doAuthenticate(allowCredentials) {
|
|
||||||
|
|
||||||
// Check if WebAuthn is supported by this browser
|
|
||||||
if (!window.PublicKeyCredential) {
|
|
||||||
$("#error").val("${msg("webauthn-unsupported-browser-text")?no_esc}");
|
|
||||||
$("#webauth").submit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let challenge = "${challenge}";
|
|
||||||
let userVerification = "${userVerification}";
|
|
||||||
let rpId = "${rpId}";
|
|
||||||
let publicKey = {
|
|
||||||
rpId : rpId,
|
|
||||||
challenge: base64url.decode(challenge, { loose: true })
|
|
||||||
};
|
|
||||||
|
|
||||||
let createTimeout = ${createTimeout};
|
|
||||||
if (createTimeout !== 0) publicKey.timeout = createTimeout * 1000;
|
|
||||||
|
|
||||||
if (allowCredentials.length) {
|
|
||||||
publicKey.allowCredentials = allowCredentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userVerification !== 'not specified') publicKey.userVerification = userVerification;
|
|
||||||
|
|
||||||
navigator.credentials.get({publicKey})
|
|
||||||
.then((result) => {
|
|
||||||
window.result = result;
|
|
||||||
|
|
||||||
let clientDataJSON = result.response.clientDataJSON;
|
|
||||||
let authenticatorData = result.response.authenticatorData;
|
|
||||||
let signature = result.response.signature;
|
|
||||||
|
|
||||||
$("#clientDataJSON").val(base64url.encode(new Uint8Array(clientDataJSON), { pad: false }));
|
|
||||||
$("#authenticatorData").val(base64url.encode(new Uint8Array(authenticatorData), { pad: false }));
|
|
||||||
$("#signature").val(base64url.encode(new Uint8Array(signature), { pad: false }));
|
|
||||||
$("#credentialId").val(result.id);
|
|
||||||
if(result.response.userHandle) {
|
|
||||||
$("#userHandle").val(base64url.encode(new Uint8Array(result.response.userHandle), { pad: false }));
|
|
||||||
}
|
|
||||||
$("#webauth").submit();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
$("#error").val(err);
|
|
||||||
$("#webauth").submit();
|
|
||||||
})
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<#elseif section = "info">
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
</@layout.registrationLayout>
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<#import "template.ftl" as layout>
|
<#import "template.ftl" as layout>
|
||||||
<#import "password-commons.ftl" as passwordCommons>
|
<#import "password-commons.ftl" as passwordCommons>
|
||||||
|
|
||||||
<@layout.registrationLayout; section>
|
<@layout.registrationLayout; section>
|
||||||
<#if section = "title">
|
<#if section = "title">
|
||||||
title
|
title
|
||||||
<#elseif section = "header">
|
<#elseif section = "header">
|
||||||
<span class="${properties.kcWebAuthnKeyIcon}"></span>
|
<span class="${properties.kcWebAuthnKeyIcon!}"></span>
|
||||||
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
|
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
@ -21,179 +21,44 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script type="text/javascript" src="${url.resourcesPath}/js/base64url.js"></script>
|
<script type="module">
|
||||||
<script type="text/javascript">
|
import { registerByWebAuthn } from "${url.resourcesPath}/js/webauthnRegister.js";
|
||||||
|
const registerButton = document.getElementById('registerWebAuthn');
|
||||||
function registerSecurityKey() {
|
registerButton.addEventListener("click", function() {
|
||||||
|
const input = {
|
||||||
// Check if WebAuthn is supported by this browser
|
challenge : '${challenge}',
|
||||||
if (!window.PublicKeyCredential) {
|
userid : '${userid}',
|
||||||
document.getElementById('error').value = '${msg("webauthn-unsupported-browser-text")?no_esc}';
|
username : '${username}',
|
||||||
document.getElementById('register').submit();
|
signatureAlgorithms : [<#list signatureAlgorithms as sigAlg>${sigAlg?c},</#list>],
|
||||||
return;
|
rpEntityName : '${rpEntityName}',
|
||||||
}
|
rpId : '${rpId}',
|
||||||
|
attestationConveyancePreference : '${attestationConveyancePreference}',
|
||||||
// mandatory parameters
|
authenticatorAttachment : '${authenticatorAttachment}',
|
||||||
let challenge = "${challenge}";
|
requireResidentKey : '${requireResidentKey}',
|
||||||
let userid = "${userid}";
|
userVerificationRequirement : '${userVerificationRequirement}',
|
||||||
let username = "${username}";
|
createTimeout : ${createTimeout},
|
||||||
|
excludeCredentialIds : '${excludeCredentialIds}',
|
||||||
let signatureAlgorithms =[<#list signatureAlgorithms as sigAlg>${sigAlg},</#list>]
|
initLabel : "${msg("webauthn-registration-init-label")?no_esc}",
|
||||||
let pubKeyCredParams = getPubKeyCredParams(signatureAlgorithms);
|
initLabelPrompt : "${msg("webauthn-registration-init-label-prompt")?no_esc}",
|
||||||
|
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
|
||||||
let rpEntityName = "${rpEntityName}";
|
|
||||||
let rp = {name: rpEntityName};
|
|
||||||
|
|
||||||
let publicKey = {
|
|
||||||
challenge: base64url.decode(challenge, {loose: true}),
|
|
||||||
rp: rp,
|
|
||||||
user: {
|
|
||||||
id: base64url.decode(userid, {loose: true}),
|
|
||||||
name: username,
|
|
||||||
displayName: username
|
|
||||||
},
|
|
||||||
pubKeyCredParams: pubKeyCredParams,
|
|
||||||
};
|
};
|
||||||
|
registerByWebAuthn(input);
|
||||||
// optional parameters
|
});
|
||||||
let rpId = "${rpId}";
|
|
||||||
publicKey.rp.id = rpId;
|
|
||||||
|
|
||||||
let attestationConveyancePreference = "${attestationConveyancePreference}";
|
|
||||||
if (attestationConveyancePreference !== 'not specified') publicKey.attestation = attestationConveyancePreference;
|
|
||||||
|
|
||||||
let authenticatorSelection = {};
|
|
||||||
let isAuthenticatorSelectionSpecified = false;
|
|
||||||
|
|
||||||
let authenticatorAttachment = "${authenticatorAttachment}";
|
|
||||||
if (authenticatorAttachment !== 'not specified') {
|
|
||||||
authenticatorSelection.authenticatorAttachment = authenticatorAttachment;
|
|
||||||
isAuthenticatorSelectionSpecified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let requireResidentKey = "${requireResidentKey}";
|
|
||||||
if (requireResidentKey !== 'not specified') {
|
|
||||||
if (requireResidentKey === 'Yes')
|
|
||||||
authenticatorSelection.requireResidentKey = true;
|
|
||||||
else
|
|
||||||
authenticatorSelection.requireResidentKey = false;
|
|
||||||
isAuthenticatorSelectionSpecified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let userVerificationRequirement = "${userVerificationRequirement}";
|
|
||||||
if (userVerificationRequirement !== 'not specified') {
|
|
||||||
authenticatorSelection.userVerification = userVerificationRequirement;
|
|
||||||
isAuthenticatorSelectionSpecified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAuthenticatorSelectionSpecified) publicKey.authenticatorSelection = authenticatorSelection;
|
|
||||||
|
|
||||||
let createTimeout = ${createTimeout};
|
|
||||||
if (createTimeout !== 0) publicKey.timeout = createTimeout * 1000;
|
|
||||||
|
|
||||||
let excludeCredentialIds = "${excludeCredentialIds}";
|
|
||||||
let excludeCredentials = getExcludeCredentials(excludeCredentialIds);
|
|
||||||
if (excludeCredentials.length > 0) publicKey.excludeCredentials = excludeCredentials;
|
|
||||||
|
|
||||||
navigator.credentials.create({publicKey})
|
|
||||||
.then(function (result) {
|
|
||||||
window.result = result;
|
|
||||||
let clientDataJSON = result.response.clientDataJSON;
|
|
||||||
let attestationObject = result.response.attestationObject;
|
|
||||||
let publicKeyCredentialId = result.rawId;
|
|
||||||
|
|
||||||
document.getElementById('clientDataJSON').value = base64url.encode(new Uint8Array(clientDataJSON), {pad: false});
|
|
||||||
document.getElementById('attestationObject').value = base64url.encode(new Uint8Array(attestationObject), {pad: false});
|
|
||||||
document.getElementById('publicKeyCredentialId').value = base64url.encode(new Uint8Array(publicKeyCredentialId), {pad: false});
|
|
||||||
|
|
||||||
if (typeof result.response.getTransports === "function") {
|
|
||||||
let transports = result.response.getTransports();
|
|
||||||
if (transports) {
|
|
||||||
document.getElementById('transports').value = getTransportsAsString(transports);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("Your browser is not able to recognize supported transport media for the authenticator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let initLabel = "WebAuthn Authenticator (Default Label)";
|
|
||||||
let labelResult = window.prompt("Please input your registered authenticator's label", initLabel);
|
|
||||||
if (labelResult === null) labelResult = initLabel;
|
|
||||||
document.getElementById("authenticatorLabel").value = labelResult;
|
|
||||||
|
|
||||||
document.getElementById('register').submit();
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
document.getElementById("error").value = err;
|
|
||||||
document.getElementById("register").submit();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPubKeyCredParams(signatureAlgorithmsList) {
|
|
||||||
let pubKeyCredParams = [];
|
|
||||||
if (signatureAlgorithmsList === []) {
|
|
||||||
pubKeyCredParams.push({type: "public-key", alg: -7});
|
|
||||||
return pubKeyCredParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < signatureAlgorithmsList.length; i++) {
|
|
||||||
pubKeyCredParams.push({
|
|
||||||
type: "public-key",
|
|
||||||
alg: signatureAlgorithmsList[i]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return pubKeyCredParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getExcludeCredentials(excludeCredentialIds) {
|
|
||||||
let excludeCredentials = [];
|
|
||||||
if (excludeCredentialIds === "") return excludeCredentials;
|
|
||||||
|
|
||||||
let excludeCredentialIdsList = excludeCredentialIds.split(',');
|
|
||||||
|
|
||||||
for (let i = 0; i < excludeCredentialIdsList.length; i++) {
|
|
||||||
excludeCredentials.push({
|
|
||||||
type: "public-key",
|
|
||||||
id: base64url.decode(excludeCredentialIdsList[i],
|
|
||||||
{loose: true})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return excludeCredentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTransportsAsString(transportsList) {
|
|
||||||
if (transportsList === '' || transportsList.constructor !== Array) return "";
|
|
||||||
|
|
||||||
let transportsString = "";
|
|
||||||
|
|
||||||
for (let i = 0; i < transportsList.length; i++) {
|
|
||||||
transportsString += transportsList[i] + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
return transportsString.slice(0, -1);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<input type="submit"
|
||||||
type="submit"
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
id="registerWebAuthn" value="${msg("doRegisterSecurityKey")}"/>
|
||||||
id="registerWebAuthn"
|
|
||||||
onclick="registerSecurityKey()"
|
|
||||||
>${msg("doRegister")}</button>
|
|
||||||
|
|
||||||
<#if !isSetRetry?has_content && isAppInitiatedAction?has_content>
|
<#if !isSetRetry?has_content && isAppInitiatedAction?has_content>
|
||||||
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-webauthn-settings-form"
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-webauthn-settings-form"
|
||||||
method="post">
|
method="post">
|
||||||
<button
|
<button type="submit"
|
||||||
type="submit"
|
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
id="cancelWebAuthnAIA" name="cancel-aia" value="true">${msg("doCancel")}
|
||||||
id="cancelWebAuthnAIA"
|
</button>
|
||||||
name="cancel-aia"
|
|
||||||
value="true"
|
|
||||||
>${msg("doCancel")}</button>
|
|
||||||
</form>
|
</form>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
Loading…
Reference in a new issue