Style improvements
This commit is contained in:
parent
a47e71fa5a
commit
f4d97717bb
|
@ -69,7 +69,6 @@
|
||||||
<td>
|
<td>
|
||||||
<#if (application.client.consentRequired && application.clientScopesGranted?has_content) || application.additionalGrants?has_content>
|
<#if (application.client.consentRequired && application.clientScopesGranted?has_content) || application.additionalGrants?has_content>
|
||||||
<button
|
<button
|
||||||
type='submit'
|
|
||||||
class='${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!}'
|
class='${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!}'
|
||||||
id='revoke-${application.client.clientId}'
|
id='revoke-${application.client.clientId}'
|
||||||
name='clientId'
|
name='clientId'
|
||||||
|
|
|
@ -38,7 +38,10 @@
|
||||||
|
|
||||||
<form action="${url.sessionsUrl}" method="post">
|
<form action="${url.sessionsUrl}" method="post">
|
||||||
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
|
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
|
||||||
<button id="logout-all-sessions" class="btn btn-default">${msg("doLogOutAllSessions")}</button>
|
<button
|
||||||
|
id="logout-all-sessions"
|
||||||
|
class="ps-button"
|
||||||
|
>${msg("doLogOutAllSessions")}</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</@layout.mainLayout>
|
</@layout.mainLayout>
|
||||||
|
|
|
@ -27,10 +27,11 @@ html {
|
||||||
|
|
||||||
.ps-container {
|
.ps-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; }
|
flex-direction: column;
|
||||||
|
align-items: stretch; }
|
||||||
.ps-container > * {
|
.ps-container > * {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1rem; }
|
margin-bottom: 1.25rem; }
|
||||||
|
|
||||||
.ps-link {
|
.ps-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -64,18 +65,38 @@ html {
|
||||||
cursor: pointer; }
|
cursor: pointer; }
|
||||||
.ps-button:hover, .ps-button:focus {
|
.ps-button:hover, .ps-button:focus {
|
||||||
border-color: var(--accent); }
|
border-color: var(--accent); }
|
||||||
|
.ps-button_primary {
|
||||||
|
border: 4px solid var(--foreground);
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-weight: bold; }
|
||||||
|
.ps-button_primary:focus, .ps-button_primary:hover {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
color: var(--background); }
|
||||||
|
|
||||||
.ps-input {
|
.ps-input {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 0.5rem;
|
||||||
border: 2px solid var(--foreground-light); }
|
border: 2px solid var(--foreground-light);
|
||||||
|
font-size: 1.2rem; }
|
||||||
.ps-input:hover, .ps-input:focus {
|
.ps-input:hover, .ps-input:focus {
|
||||||
border-color: var(--accent); }
|
border-color: var(--accent); }
|
||||||
|
.ps-input:focus {
|
||||||
|
color: var(--background);
|
||||||
|
background-color: var(--foreground); }
|
||||||
|
.ps-input:disabled {
|
||||||
|
background-color: var(--background-alt-2); }
|
||||||
|
.ps-input:disabled:hover {
|
||||||
|
border-color: var(--foreground-light); }
|
||||||
|
|
||||||
.ps-form-group {
|
.ps-form-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; }
|
flex-direction: column; }
|
||||||
.ps-form-group--label {
|
.ps-form-group--label {
|
||||||
display: flex; }
|
margin-bottom: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
font-weight: bold; }
|
||||||
|
.ps-form-group .ps-button {
|
||||||
|
align-self: flex-start; }
|
||||||
|
|
||||||
.ps-main {
|
.ps-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -171,7 +192,8 @@ html {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: white;
|
background: white;
|
||||||
word-break: break-word;
|
overflow-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
pointer-events: all; }
|
pointer-events: all; }
|
||||||
@media screen and (min-width: 1200px) {
|
@media screen and (min-width: 1200px) {
|
||||||
.ps-page--section {
|
.ps-page--section {
|
||||||
|
|
|
@ -11,4 +11,17 @@
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_primary {
|
||||||
|
border: 4px solid var(--foreground);
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
.ps-container {
|
.ps-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
&--label {
|
&--label {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps-button {
|
||||||
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
.ps-input {
|
.ps-input {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 0.5rem;
|
||||||
border: 2px solid var(--foreground-light);
|
border: 2px solid var(--foreground-light);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
color: var(--background);
|
||||||
|
background-color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--background-alt-2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--foreground-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: white;
|
background: white;
|
||||||
word-break: break-word;
|
overflow-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
|
||||||
@media screen and (min-width: 1200px) {
|
@media screen and (min-width: 1200px) {
|
||||||
|
|
3
common/resources/scss/table.scss
Normal file
3
common/resources/scss/table.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.ps-table {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
7
login/cli_splash.ftl
Normal file
7
login/cli_splash.ftl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
_ __ _ _
|
||||||
|
| |/ /___ _ _ ___| | ___ __ _| | __
|
||||||
|
| ' // _ \ | | |/ __| |/ _ \ / _` | |/ /
|
||||||
|
| . \ __/ |_| | (__| | (_) | (_| | <
|
||||||
|
|_|\_\___|\__, |\___|_|\___/ \__,_|_|\_\
|
||||||
|
|___/
|
||||||
|
|
19
login/code.ftl
Executable file
19
login/code.ftl
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
<#if code.success>
|
||||||
|
${msg("codeSuccessTitle")}
|
||||||
|
<#else>
|
||||||
|
${msg("codeErrorTitle", code.error)}
|
||||||
|
</#if>
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-code">
|
||||||
|
<#if code.success>
|
||||||
|
<p>${msg("copyCodeInstruction")}</p>
|
||||||
|
<input id="code" class="${properties.kcTextareaClass!}" value="${code.code}"/>
|
||||||
|
<#else>
|
||||||
|
<p id="error">${code.error}</p>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
33
login/delete-account-confirm.ftl
Normal file
33
login/delete-account-confirm.ftl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("deleteAccountConfirm")}
|
||||||
|
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<form action="${url.loginAction}" class="form-vertical" method="post">
|
||||||
|
|
||||||
|
<div class="alert alert-warning" style="margin-top:0 !important;margin-bottom:30px !important">
|
||||||
|
<span class="pficon pficon-warning-triangle-o"></span>
|
||||||
|
${msg("irreversibleAction")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>${msg("deletingImplies")}</p>
|
||||||
|
<ul style="color: #72767b;list-style: disc;list-style-position: inside;">
|
||||||
|
<li>${msg("loggingOutImmediately")}</li>
|
||||||
|
<li>${msg("errasingData")}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="delete-account-text">${msg("finalDeletionConfirmation")}</p>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doConfirmDelete")}" />
|
||||||
|
<#if triggered_from_aia>
|
||||||
|
<button class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" style="margin-left: calc(100% - 220px)" type="submit" name="cancel-aia" value="true" />${msg("doCancel")}</button>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</@layout.registrationLayout>
|
16
login/error.ftl
Executable file
16
login/error.ftl
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=false; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${kcSanitize(msg("errorTitle"))?no_esc}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-error-message">
|
||||||
|
<p class="instruction">${kcSanitize(message.summary)?no_esc}</p>
|
||||||
|
<#if skipLink??>
|
||||||
|
<#else>
|
||||||
|
<#if client?? && client.baseUrl?has_content>
|
||||||
|
<p><a id="backToApplication" href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
30
login/frontchannel-logout.ftl
Normal file
30
login/frontchannel-logout.ftl
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
<script>
|
||||||
|
document.title = "${msg("frontchannel-logout.title")}";
|
||||||
|
</script>
|
||||||
|
${msg("frontchannel-logout.title")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<p>${msg("frontchannel-logout.message")}</p>
|
||||||
|
<ul>
|
||||||
|
<#list logout.clients as client>
|
||||||
|
<li>
|
||||||
|
${client.name}
|
||||||
|
<iframe src="${client.frontChannelLogoutUrl}" style="display:none;"></iframe>
|
||||||
|
</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
<#if logout.logoutRedirectUri?has_content>
|
||||||
|
<script>
|
||||||
|
function readystatechange(event) {
|
||||||
|
if (document.readyState=='complete') {
|
||||||
|
window.location.replace('${logout.logoutRedirectUri}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('readystatechange', readystatechange);
|
||||||
|
</script>
|
||||||
|
<a id="continue" class="btn btn-primary" href="${logout.logoutRedirectUri}">${msg("doContinue")}</a>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
23
login/idp-review-user-profile.ftl
Normal file
23
login/idp-review-user-profile.ftl
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("loginIdpReviewProfileTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-idp-review-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
|
||||||
|
<@userProfileCommons.userProfileFormFields/>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
24
login/info.ftl
Executable file
24
login/info.ftl
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=false; section>
|
||||||
|
<#if section = "header">
|
||||||
|
<#if messageHeader??>
|
||||||
|
${messageHeader}
|
||||||
|
<#else>
|
||||||
|
${message.summary}
|
||||||
|
</#if>
|
||||||
|
<#elseif section = "form">
|
||||||
|
<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>
|
||||||
|
<#if skipLink??>
|
||||||
|
<#else>
|
||||||
|
<#if pageRedirectUri?has_content>
|
||||||
|
<p><a href="${pageRedirectUri}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||||
|
<#elseif actionUri?has_content>
|
||||||
|
<p><a href="${actionUri}">${kcSanitize(msg("proceedWithAction"))?no_esc}</a></p>
|
||||||
|
<#elseif (client.baseUrl)?has_content>
|
||||||
|
<p><a href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
108
login/login-config-totp.ftl
Executable file
108
login/login-config-totp.ftl
Executable file
|
@ -0,0 +1,108 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayRequiredFields=false displayMessage=!messagesPerField.existsError('totp','userLabel'); section>
|
||||||
|
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("loginTotpTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<ol id="kc-totp-settings">
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep1")}</p>
|
||||||
|
|
||||||
|
<ul id="kc-totp-supported-apps">
|
||||||
|
<#list totp.supportedApplications as app>
|
||||||
|
<li>${msg(app)}</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<#if mode?? && mode = "manual">
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpManualStep2")}</p>
|
||||||
|
<p><span id="kc-totp-secret-key">${totp.totpSecretEncoded}</span></p>
|
||||||
|
<p><a href="${totp.qrUrl}" id="mode-barcode">${msg("loginTotpScanBarcode")}</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpManualStep3")}</p>
|
||||||
|
<p>
|
||||||
|
<ul>
|
||||||
|
<li id="kc-totp-type">${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}</li>
|
||||||
|
<li id="kc-totp-algorithm">${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}</li>
|
||||||
|
<li id="kc-totp-digits">${msg("loginTotpDigits")}: ${totp.policy.digits}</li>
|
||||||
|
<#if totp.policy.type = "totp">
|
||||||
|
<li id="kc-totp-period">${msg("loginTotpInterval")}: ${totp.policy.period}</li>
|
||||||
|
<#elseif totp.policy.type = "hotp">
|
||||||
|
<li id="kc-totp-counter">${msg("loginTotpCounter")}: ${totp.policy.initialCounter}</li>
|
||||||
|
</#if>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<#else>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep2")}</p>
|
||||||
|
<img id="kc-totp-secret-qr-code" src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
|
||||||
|
<p><a href="${totp.manualUrl}" id="mode-manual">${msg("loginTotpUnableToScan")}</a></p>
|
||||||
|
</li>
|
||||||
|
</#if>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep3")}</p>
|
||||||
|
<p>${msg("loginTotpStep3DeviceName")}</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<label for="totp" class="control-label">${msg("authenticatorCode")}</label> <span class="required">*</span>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input type="text" id="totp" name="totp" autocomplete="off" class="${properties.kcInputClass!}"
|
||||||
|
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>
|
||||||
|
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
||||||
|
<#if mode??><input type="hidden" id="mode" name="mode" value="${mode}"/></#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<label for="userLabel" class="control-label">${msg("loginTotpDeviceName")}</label> <#if totp.otpCredentials?size gte 1><span class="required">*</span></#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input type="text" class="${properties.kcInputClass!}" id="userLabel" name="userLabel" autocomplete="off"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('userLabel')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('userLabel')>
|
||||||
|
<span id="input-error-otp-label" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('userLabel'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<input type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="saveTOTPBtn" value="${msg("doSubmit")}"
|
||||||
|
/>
|
||||||
|
<button type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="cancelTOTPBtn" name="cancel-aia" value="true" />${msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
<#else>
|
||||||
|
<input type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="saveTOTPBtn" value="${msg("doSubmit")}"
|
||||||
|
/>
|
||||||
|
</#if>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
13
login/login-idp-link-confirm.ftl
Normal file
13
login/login-idp-link-confirm.ftl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("confirmLinkIdpTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-register-form" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="updateProfile" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
|
||||||
|
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="linkAccount" value="linkAccount">${msg("confirmLinkIdpContinue", idpDisplayName)}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
16
login/login-idp-link-email.ftl
Normal file
16
login/login-idp-link-email.ftl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("emailLinkIdpTitle", idpDisplayName)}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<p id="instruction1" class="instruction">
|
||||||
|
${msg("emailLinkIdp1", idpDisplayName, brokerContext.username, realm.displayName)}
|
||||||
|
</p>
|
||||||
|
<p id="instruction2" class="instruction">
|
||||||
|
${msg("emailLinkIdp2")} <a href="${url.loginAction}">${msg("doClickHere")}</a> ${msg("emailLinkIdp3")}
|
||||||
|
</p>
|
||||||
|
<p id="instruction3" class="instruction">
|
||||||
|
${msg("emailLinkIdp4")} <a href="${url.loginAction}">${msg("doClickHere")}</a> ${msg("emailLinkIdp5")}
|
||||||
|
</p>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
68
login/login-oauth-grant.ftl
Executable file
68
login/login-oauth-grant.ftl
Executable file
|
@ -0,0 +1,68 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout bodyClass="oauth"; section>
|
||||||
|
<#if section = "header">
|
||||||
|
<#if client.attributes.logoUri??>
|
||||||
|
<img src="${client.attributes.logoUri}"/>
|
||||||
|
</#if>
|
||||||
|
<p>
|
||||||
|
<#if client.name?has_content>
|
||||||
|
${msg("oauthGrantTitle",advancedMsg(client.name))}
|
||||||
|
<#else>
|
||||||
|
${msg("oauthGrantTitle",client.clientId)}
|
||||||
|
</#if>
|
||||||
|
</p>
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-oauth" class="content-area">
|
||||||
|
<h3>${msg("oauthGrantRequest")}</h3>
|
||||||
|
<ul>
|
||||||
|
<#if oauth.clientScopesRequested??>
|
||||||
|
<#list oauth.clientScopesRequested as clientScope>
|
||||||
|
<li>
|
||||||
|
<span><#if !clientScope.dynamicScopeParameter??>
|
||||||
|
${advancedMsg(clientScope.consentScreenText)}
|
||||||
|
<#else>
|
||||||
|
${advancedMsg(clientScope.consentScreenText)}: <b>${clientScope.dynamicScopeParameter}</b>
|
||||||
|
</#if>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</ul>
|
||||||
|
<#if client.attributes.policyUri?? || client.attributes.tosUri??>
|
||||||
|
<h3>
|
||||||
|
<#if client.name?has_content>
|
||||||
|
${msg("oauthGrantInformation",advancedMsg(client.name))}
|
||||||
|
<#else>
|
||||||
|
${msg("oauthGrantInformation",client.clientId)}
|
||||||
|
</#if>
|
||||||
|
<#if client.attributes.tosUri??>
|
||||||
|
${msg("oauthGrantReview")}
|
||||||
|
<a href="${client.attributes.tosUri}" target="_blank">${msg("oauthGrantTos")}</a>
|
||||||
|
</#if>
|
||||||
|
<#if client.attributes.policyUri??>
|
||||||
|
${msg("oauthGrantReview")}
|
||||||
|
<a href="${client.attributes.policyUri}" target="_blank">${msg("oauthGrantPolicy")}</a>
|
||||||
|
</#if>
|
||||||
|
</h3>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<form class="form-actions" action="${url.oauthAction}" method="POST">
|
||||||
|
<input type="hidden" name="code" value="${oauth.code}">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons">
|
||||||
|
<div class="${properties.kcFormButtonsWrapperClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="accept" id="kc-login" type="submit" value="${msg("doYes")}"/>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doNo")}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
31
login/login-oauth2-device-verify-user-code.ftl
Normal file
31
login/login-oauth2-device-verify-user-code.ftl
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("oauth2DeviceVerificationTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-user-verify-device-user-code-form" class="${properties.kcFormClass!}" action="${url.oauth2DeviceVerificationAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="device-user-code" class="${properties.kcLabelClass!}">${msg("verifyOAuth2DeviceUserCode")}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input id="device-user-code" name="device_user_code" autocomplete="off" type="text" class="${properties.kcInputClass!}" autofocus />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<div class="${properties.kcFormButtonsWrapperClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
58
login/login-otp.ftl
Executable file
58
login/login-otp.ftl
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
|
||||||
|
<#if section="header">
|
||||||
|
${msg("doLogIn")}
|
||||||
|
<#elseif section="form">
|
||||||
|
<form id="kc-otp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
|
||||||
|
method="post">
|
||||||
|
<#if otpLogin.userOtpCredentials?size gt 1>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<#list otpLogin.userOtpCredentials as otpCredential>
|
||||||
|
<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>>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</#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 class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
11
login/login-page-expired.ftl
Normal file
11
login/login-page-expired.ftl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("pageExpiredTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<p id="instruction1" class="instruction">
|
||||||
|
${msg("pageExpiredMsg1")} <a id="loginRestartLink" href="${url.loginRestartFlowUrl}">${msg("doClickHere")}</a> .<br/>
|
||||||
|
${msg("pageExpiredMsg2")} <a id="loginContinueLink" href="${url.loginAction}">${msg("doClickHere")}</a> .
|
||||||
|
</p>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
43
login/login-password.ftl
Executable file
43
login/login-password.ftl
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("doLogIn")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-form">
|
||||||
|
<div id="kc-form-wrapper">
|
||||||
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
|
||||||
|
method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||||
|
<hr/>
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||||
|
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
|
||||||
|
type="password" autocomplete="on" autofocus
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
||||||
|
/>
|
||||||
|
<#if messagesPerField.existsError('password')>
|
||||||
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
|
<div id="kc-form-options">
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<#if realm.resetPasswordAllowed>
|
||||||
|
<span><a tabindex="5"
|
||||||
|
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
|
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</@layout.registrationLayout>
|
184
login/login-recovery-authn-code-config.ftl
Normal file
184
login/login-recovery-authn-code-config.ftl
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("recovery-code-config-header")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<!-- warning -->
|
||||||
|
<div class="pf-c-alert pf-m-warning pf-m-inline ${properties.kcRecoveryCodesWarning}" aria-label="Warning alert">
|
||||||
|
<div class="pf-c-alert__icon">
|
||||||
|
<i class="pficon-warning-triangle-o" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<h4 class="pf-c-alert__title">
|
||||||
|
<span class="pf-screen-reader">Warning alert:</span>
|
||||||
|
${msg("recovery-code-config-warning-title")}
|
||||||
|
</h4>
|
||||||
|
<div class="pf-c-alert__description">
|
||||||
|
<p>${msg("recovery-code-config-warning-message")}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol id="kc-recovery-codes-list" class="${properties.kcRecoveryCodesList!}">
|
||||||
|
<#list recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList as code>
|
||||||
|
<li><span>${code?counter}:</span> ${code[0..3]}-${code[4..7]}-${code[8..]}</li>
|
||||||
|
</#list>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<!-- actions -->
|
||||||
|
<div class="${properties.kcRecoveryCodesActions}">
|
||||||
|
<button id="printRecoveryCodes" class="pf-c-button pf-m-link" type="button">
|
||||||
|
<i class="pficon-print"></i> ${msg("recovery-codes-print")}
|
||||||
|
</button>
|
||||||
|
<button id="downloadRecoveryCodes" class="pf-c-button pf-m-link" type="button">
|
||||||
|
<i class="pficon-save"></i> ${msg("recovery-codes-download")}
|
||||||
|
</button>
|
||||||
|
<button id="copyRecoveryCodes" class="pf-c-button pf-m-link" type="button">
|
||||||
|
<i class="pficon-blueprint"></i> ${msg("recovery-codes-copy")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- confirmation checkbox -->
|
||||||
|
<div class="${properties.kcCheckClass} ${properties.kcRecoveryCodesConfirmation}">
|
||||||
|
<input class="${properties.kcCheckInputClass}" type="checkbox" id="kcRecoveryCodesConfirmationCheck" name="kcRecoveryCodesConfirmationCheck"
|
||||||
|
onchange="document.getElementById('saveRecoveryAuthnCodesBtn').disabled = !this.checked;"
|
||||||
|
/>
|
||||||
|
<label class="${properties.kcCheckLabelClass}" for="kcRecoveryCodesConfirmationCheck">${msg("recovery-codes-confirmation-message")}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-recovery-codes-settings-form" method="post">
|
||||||
|
<input type="hidden" name="generatedRecoveryAuthnCodes" value="${recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesAsString}" />
|
||||||
|
<input type="hidden" name="generatedAt" value="${recoveryAuthnCodesConfigBean.generatedAt?c}" />
|
||||||
|
<input type="hidden" id="userLabel" name="userLabel" value="${msg("recovery-codes-label-default")}" />
|
||||||
|
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<input type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="saveRecoveryAuthnCodesBtn" value="${msg("recovery-codes-action-complete")}"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<button type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="cancelRecoveryAuthnCodesBtn" name="cancel-aia" value="true" />${msg("recovery-codes-action-cancel")}
|
||||||
|
</button>
|
||||||
|
<#else>
|
||||||
|
<input type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="saveRecoveryAuthnCodesBtn" value="${msg("recovery-codes-action-complete")}"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</#if>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* copy recovery codes */
|
||||||
|
function copyRecoveryCodes() {
|
||||||
|
var tmpTextarea = document.createElement("textarea");
|
||||||
|
var codes = document.getElementById("kc-recovery-codes-list").getElementsByTagName("li");
|
||||||
|
for (i = 0; i < codes.length; i++) {
|
||||||
|
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\n";
|
||||||
|
}
|
||||||
|
document.body.appendChild(tmpTextarea);
|
||||||
|
tmpTextarea.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(tmpTextarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyButton = document.getElementById("copyRecoveryCodes");
|
||||||
|
copyButton && copyButton.addEventListener("click", function () {
|
||||||
|
copyRecoveryCodes();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* download recovery codes */
|
||||||
|
function formatCurrentDateTime() {
|
||||||
|
var dt = new Date();
|
||||||
|
var options = {
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
timeZoneName: 'short'
|
||||||
|
};
|
||||||
|
|
||||||
|
return dt.toLocaleString('en-US', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRecoveryCodeList() {
|
||||||
|
var recoveryCodes = document.querySelectorAll(".kc-recovery-codes-list li");
|
||||||
|
var recoveryCodeList = "";
|
||||||
|
|
||||||
|
for (var i = 0; i < recoveryCodes.length; i++) {
|
||||||
|
var recoveryCodeLiElement = recoveryCodes[i].innerText;
|
||||||
|
recoveryCodeList += recoveryCodeLiElement + "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return recoveryCodeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDownloadContent() {
|
||||||
|
var recoveryCodeList = parseRecoveryCodeList();
|
||||||
|
var dt = new Date();
|
||||||
|
var options = {
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
timeZoneName: 'short'
|
||||||
|
};
|
||||||
|
|
||||||
|
return fileBodyContent =
|
||||||
|
"${msg("recovery-codes-download-file-header")}\n\n" +
|
||||||
|
recoveryCodeList + "\n" +
|
||||||
|
"${msg("recovery-codes-download-file-description")}\n\n" +
|
||||||
|
"${msg("recovery-codes-download-file-date")} " + formatCurrentDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUpDownloadLinkAndDownload(filename, text) {
|
||||||
|
var el = document.createElement('a');
|
||||||
|
el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
||||||
|
el.setAttribute('download', filename);
|
||||||
|
el.style.display = 'none';
|
||||||
|
document.body.appendChild(el);
|
||||||
|
el.click();
|
||||||
|
document.body.removeChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadRecoveryCodes() {
|
||||||
|
setUpDownloadLinkAndDownload('kc-download-recovery-codes.txt', buildDownloadContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadButton = document.getElementById("downloadRecoveryCodes");
|
||||||
|
downloadButton && downloadButton.addEventListener("click", downloadRecoveryCodes);
|
||||||
|
|
||||||
|
/* print recovery codes */
|
||||||
|
function buildPrintContent() {
|
||||||
|
var recoveryCodeListHTML = document.getElementById('kc-recovery-codes-list').innerHTML;
|
||||||
|
var styles =
|
||||||
|
`@page { size: auto; margin-top: 0; }
|
||||||
|
body { width: 480px; }
|
||||||
|
div { list-style-type: none; font-family: monospace }
|
||||||
|
p:first-of-type { margin-top: 48px }`
|
||||||
|
|
||||||
|
return printFileContent =
|
||||||
|
"<html><style>" + styles + "</style><body>" +
|
||||||
|
"<title>kc-download-recovery-codes</title>" +
|
||||||
|
"<p>${msg("recovery-codes-download-file-header")}</p>" +
|
||||||
|
"<div>" + recoveryCodeListHTML + "</div>" +
|
||||||
|
"<p>${msg("recovery-codes-download-file-description")}</p>" +
|
||||||
|
"<p>${msg("recovery-codes-download-file-date")} " + formatCurrentDateTime() + "</p>" +
|
||||||
|
"</body></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
function printRecoveryCodes() {
|
||||||
|
var w = window.open();
|
||||||
|
w.document.write(buildPrintContent());
|
||||||
|
w.print();
|
||||||
|
w.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
var printButton = document.getElementById("printRecoveryCodes");
|
||||||
|
printButton && printButton.addEventListener("click", printRecoveryCodes);
|
||||||
|
</script>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
32
login/login-recovery-authn-code-input.ftl
Normal file
32
login/login-recovery-authn-code-input.ftl
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("auth-recovery-code-header")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-recovery-code-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="recoveryCodeInput" class="${properties.kcLabelClass!}">${msg("auth-recovery-code-prompt", recoveryAuthnCodesInputBean.codeNumber?c)}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input id="recoveryCodeInput" name="recoveryCodeInput" autocomplete="off" type="text" class="${properties.kcInputClass!}" autofocus/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
50
login/login-reset-password.ftl
Executable file
50
login/login-reset-password.ftl
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayInfo=true displayMessage=!messagesPerField.existsError('username'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("emailForgotTitle")}
|
||||||
|
<#elseif section = "info" >
|
||||||
|
<#if realm.duplicateEmailsAllowed>
|
||||||
|
${msg("emailInstructionUsername")}
|
||||||
|
<#else>
|
||||||
|
${msg("emailInstruction")}
|
||||||
|
</#if>
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-reset-password-form" class="ps-container" action="${url.loginAction}" method="post">
|
||||||
|
<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
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
class="${properties.kcInputClass!}"
|
||||||
|
autofocus
|
||||||
|
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 class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
71
login/login-update-password.ftl
Executable file
71
login/login-update-password.ftl
Executable file
|
@ -0,0 +1,71 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password','password-confirm'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("updatePasswordTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<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.kcLabelWrapperClass!}">
|
||||||
|
<label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
|
||||||
|
autofocus autocomplete="new-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password')>
|
||||||
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input type="password" id="password-confirm" name="password-confirm"
|
||||||
|
class="${properties.kcInputClass!}"
|
||||||
|
autocomplete="new-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password-confirm')>
|
||||||
|
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label><input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked> ${msg("logoutOtherSessions")}</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<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>
|
||||||
|
<#else>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
99
login/login-update-profile.ftl
Executable file
99
login/login-update-profile.ftl
Executable file
|
@ -0,0 +1,99 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','email','firstName','lastName'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("loginProfileTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<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')>
|
||||||
|
<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 id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<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>
|
||||||
|
<#else>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
87
login/login-username.ftl
Executable file
87
login/login-username.ftl
Executable file
|
@ -0,0 +1,87 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("loginAccountTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-form">
|
||||||
|
<div id="kc-form-wrapper">
|
||||||
|
<#if realm.password>
|
||||||
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
|
||||||
|
method="post">
|
||||||
|
<#if !usernameHidden??>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="username"
|
||||||
|
class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
|
||||||
|
|
||||||
|
<input tabindex="1" id="username"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||||
|
class="${properties.kcInputClass!}" name="username"
|
||||||
|
value="${(login.username!'')}"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
|
<div id="kc-form-options">
|
||||||
|
<#if realm.rememberMe && !usernameHidden??>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<#if login.rememberMe??>
|
||||||
|
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"
|
||||||
|
checked> ${msg("rememberMe")}
|
||||||
|
<#else>
|
||||||
|
<input tabindex="3" id="rememberMe" name="rememberMe"
|
||||||
|
type="checkbox"> ${msg("rememberMe")}
|
||||||
|
</#if>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
|
<input tabindex="4"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#elseif section = "info" >
|
||||||
|
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
|
</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>
|
14
login/login-verify-email.ftl
Executable file
14
login/login-verify-email.ftl
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayInfo=true; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("emailVerifyTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<p class="instruction">${msg("emailVerifyInstruction1",user.email)}</p>
|
||||||
|
<#elseif section = "info">
|
||||||
|
<p class="instruction">
|
||||||
|
${msg("emailVerifyInstruction2")}
|
||||||
|
<br/>
|
||||||
|
<a href="${url.loginAction}">${msg("doClickHere")}</a> ${msg("emailVerifyInstruction3")}
|
||||||
|
</p>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
55
login/login-x509-info.ftl
Normal file
55
login/login-x509-info.ftl
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("doLogIn")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<form id="kc-x509-login-info" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="certificate_subjectDN" class="${properties.kcLabelClass!}">${msg("clientCertificate")}</label>
|
||||||
|
</div>
|
||||||
|
<#if x509.formData.subjectDN??>
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label id="certificate_subjectDN" class="${properties.kcLabelClass!}">${(x509.formData.subjectDN!"")}</label>
|
||||||
|
</div>
|
||||||
|
<#else>
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label id="certificate_subjectDN" class="${properties.kcLabelClass!}">${msg("noCertificate")}</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
|
||||||
|
<#if x509.formData.isUserEnabled??>
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="username" class="${properties.kcLabelClass!}">${msg("doX509Login")}</label>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label id="username" class="${properties.kcLabelClass!}">${(x509.formData.username!'')}</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<div class="${properties.kcFormButtonsWrapperClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doContinue")}"/>
|
||||||
|
<#if x509.formData.isUserEnabled??>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doIgnore")}"/>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</@layout.registrationLayout>
|
|
@ -58,6 +58,11 @@
|
||||||
</span>
|
</span>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
|
<#if realm.resetPasswordAllowed>
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
|
@ -72,11 +77,6 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</#if>
|
</#if>
|
||||||
<#if realm.resetPasswordAllowed>
|
|
||||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
|
||||||
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
|
||||||
</div>
|
|
||||||
</#if>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
|
|
38
login/logout-confirm.ftl
Normal file
38
login/logout-confirm.ftl
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("logoutConfirmTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-logout-confirm" class="content-area">
|
||||||
|
<p class="instruction">${msg("logoutConfirmHeader")}</p>
|
||||||
|
|
||||||
|
<form class="form-actions" action="${url.logoutConfirmAction}" method="POST">
|
||||||
|
<input type="hidden" name="session_code" value="${logoutConfirm.code}">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
|
<input tabindex="4"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
name="confirmLogout" id="kc-logout" type="submit" value="${msg("doLogout")}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="kc-info-message">
|
||||||
|
<#if logoutConfirm.skipLink>
|
||||||
|
<#else>
|
||||||
|
<#if (client.baseUrl)?has_content>
|
||||||
|
<p><a href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
62
login/register-user-profile.ftl
Executable file
62
login/register-user-profile.ftl
Executable file
|
@ -0,0 +1,62 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("registerTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-register-form" class="ps-container" action="${url.registrationAction}" method="post">
|
||||||
|
|
||||||
|
<@userProfileCommons.userProfileFormFields; callback, attribute>
|
||||||
|
<#if callback = "afterField">
|
||||||
|
<#-- render password fields just under the username or email (if used as username) -->
|
||||||
|
<#if passwordRequired?? && (attribute.name == 'username' || (attribute.name == 'email' && realm.registrationEmailAsUsername))>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label> *
|
||||||
|
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password')>
|
||||||
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password-confirm"
|
||||||
|
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label> *
|
||||||
|
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||||
|
name="password-confirm"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password-confirm')>
|
||||||
|
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</@userProfileCommons.userProfileFormFields>
|
||||||
|
|
||||||
|
<#if recaptchaRequired??>
|
||||||
|
<div class="ps-form-group">
|
||||||
|
<div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
112
login/register.ftl
Executable file
112
login/register.ftl
Executable file
|
@ -0,0 +1,112 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('firstName','lastName','email','username','password','password-confirm'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("registerTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-register-form" class="ps-container" action="${url.registrationAction}" method="post">
|
||||||
|
<!--div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
|
||||||
|
<input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName"
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
|
||||||
|
<input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName"
|
||||||
|
value="${(register.formData.lastName!'')}"
|
||||||
|
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-->
|
||||||
|
|
||||||
|
<#if !realm.registrationEmailAsUsername>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
|
||||||
|
<input type="text" id="username" class="${properties.kcInputClass!}" name="username"
|
||||||
|
value="${(register.formData.username!'')}" autocomplete="username"
|
||||||
|
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>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<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')>
|
||||||
|
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('email'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if passwordRequired??>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||||
|
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password')>
|
||||||
|
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password-confirm"
|
||||||
|
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||||
|
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||||
|
name="password-confirm"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('password-confirm')>
|
||||||
|
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#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>
|
25
login/saml-post-form.ftl
Normal file
25
login/saml-post-form.ftl
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("saml.post-form.title")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<script>window.onload = function() {document.forms[0].submit()};</script>
|
||||||
|
<p>${msg("saml.post-form.message")}</p>
|
||||||
|
<form name="saml-post-binding" method="post" action="${samlPost.url}">
|
||||||
|
<#if samlPost.SAMLRequest??>
|
||||||
|
<input type="hidden" name="SAMLRequest" value="${samlPost.SAMLRequest}"/>
|
||||||
|
</#if>
|
||||||
|
<#if samlPost.SAMLResponse??>
|
||||||
|
<input type="hidden" name="SAMLResponse" value="${samlPost.SAMLResponse}"/>
|
||||||
|
</#if>
|
||||||
|
<#if samlPost.relayState??>
|
||||||
|
<input type="hidden" name="RelayState" value="${samlPost.relayState}"/>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<p>${msg("saml.post-form.js-disabled")}</p>
|
||||||
|
<input type="submit" value="${msg("doContinue")}"/>
|
||||||
|
</noscript>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
43
login/select-authenticator.ftl
Normal file
43
login/select-authenticator.ftl
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayInfo=false; section>
|
||||||
|
<#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">
|
||||||
|
${msg("loginChooseAuthenticator")}
|
||||||
|
</#if>
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<form id="kc-select-credential-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcSelectAuthListClass!}">
|
||||||
|
<#list auth.authenticationSelections as authenticationSelection>
|
||||||
|
<div class="${properties.kcSelectAuthListItemClass!}" onclick="fillAndSubmit('${authenticationSelection.authExecId}')">
|
||||||
|
|
||||||
|
<div class="${properties.kcSelectAuthListItemIconClass!}">
|
||||||
|
<i class="${properties['${authenticationSelection.iconCssClass}']!authenticationSelection.iconCssClass} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemBodyClass!}">
|
||||||
|
<div class="${properties.kcSelectAuthListItemHeadingClass!}">
|
||||||
|
${msg('${authenticationSelection.displayName}')}
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
${msg('${authenticationSelection.helpText}')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemArrowClass!}">
|
||||||
|
<i class="${properties.kcSelectAuthListItemArrowIconClass!}"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#list>
|
||||||
|
<input type="hidden" id="authexec-hidden-input" name="authenticationExecution" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
||||||
|
|
15
login/terms.ftl
Executable file
15
login/terms.ftl
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=false; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("termsTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-terms-text">
|
||||||
|
${kcSanitize(msg("termsText"))?no_esc}
|
||||||
|
</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("doAccept")}"/>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-decline" type="submit" value="${msg("doDecline")}"/>
|
||||||
|
</form>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
|
@ -3,6 +3,8 @@ import=common/pub.solar
|
||||||
|
|
||||||
styles=
|
styles=
|
||||||
|
|
||||||
|
kcInputClass=ps-input
|
||||||
|
|
||||||
kcButtonClass=ps-button
|
kcButtonClass=ps-button
|
||||||
kcButtonPrimaryClass=ps-button_primary
|
kcButtonPrimaryClass=ps-button_primary
|
||||||
kcButtonLargeClass=ps-button_large
|
kcButtonLargeClass=ps-button_large
|
||||||
|
|
42
login/update-email.ftl
Normal file
42
login/update-email.ftl
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('email'); section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("updateEmailTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-update-email-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
<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="${(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 id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<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>
|
||||||
|
<#else>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
28
login/update-user-profile.ftl
Executable file
28
login/update-user-profile.ftl
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<#import "user-profile-commons.ftl" as userProfileCommons>
|
||||||
|
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${msg("loginProfileTitle")}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
|
||||||
|
<@userProfileCommons.userProfileFormFields/>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<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" formnovalidate/>${msg("doCancel")}</button>
|
||||||
|
<#else>
|
||||||
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
187
login/user-profile-commons.ftl
Normal file
187
login/user-profile-commons.ftl
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<#macro userProfileFormFields>
|
||||||
|
<#assign currentGroup="">
|
||||||
|
|
||||||
|
<#list profile.attributes as attribute>
|
||||||
|
|
||||||
|
<#assign groupName = attribute.group!"">
|
||||||
|
<#if groupName != currentGroup>
|
||||||
|
<#assign currentGroup=groupName>
|
||||||
|
<#if currentGroup != "" >
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
|
||||||
|
<#assign groupDisplayHeader=attribute.groupDisplayHeader!"">
|
||||||
|
<#if groupDisplayHeader != "">
|
||||||
|
<#assign groupHeaderText=advancedMsg(attribute.groupDisplayHeader)!groupName>
|
||||||
|
<#else>
|
||||||
|
<#assign groupHeaderText=groupName>
|
||||||
|
</#if>
|
||||||
|
<div class="${properties.kcContentWrapperClass!}">
|
||||||
|
<label id="header-${groupName}" class="${kcFormGroupHeader!}">${groupHeaderText}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#assign groupDisplayDescription=attribute.groupDisplayDescription!"">
|
||||||
|
<#if groupDisplayDescription != "">
|
||||||
|
<#assign groupDescriptionText=advancedMsg(attribute.groupDisplayDescription)!"">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label id="description-${groupName}" class="${properties.kcLabelClass!}">${groupDescriptionText}</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#nested "beforeField" attribute>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!}">
|
||||||
|
<label for="${attribute.name}" class="${properties.kcLabelClass!}">${advancedMsg(attribute.displayName!'')}</label>
|
||||||
|
<#if attribute.required>*</#if>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<#if attribute.annotations.inputHelperTextBefore??>
|
||||||
|
<div class="${properties.kcInputHelperTextBeforeClass!}" id="form-help-text-before-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}</div>
|
||||||
|
</#if>
|
||||||
|
<@inputFieldByType attribute=attribute/>
|
||||||
|
<#if messagesPerField.existsError('${attribute.name}')>
|
||||||
|
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
<#if attribute.annotations.inputHelperTextAfter??>
|
||||||
|
<div class="${properties.kcInputHelperTextAfterClass!}" id="form-help-text-after-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<#nested "afterField" attribute>
|
||||||
|
</#list>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro inputFieldByType attribute>
|
||||||
|
<#switch attribute.annotations.inputType!''>
|
||||||
|
<#case 'textarea'>
|
||||||
|
<@textareaTag attribute=attribute/>
|
||||||
|
<#break>
|
||||||
|
<#case 'select'>
|
||||||
|
<#case 'multiselect'>
|
||||||
|
<@selectTag attribute=attribute/>
|
||||||
|
<#break>
|
||||||
|
<#case 'select-radiobuttons'>
|
||||||
|
<#case 'multiselect-checkboxes'>
|
||||||
|
<@inputTagSelects attribute=attribute/>
|
||||||
|
<#break>
|
||||||
|
<#default>
|
||||||
|
<@inputTag attribute=attribute/>
|
||||||
|
</#switch>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro inputTag attribute>
|
||||||
|
<input type="<@inputTagType attribute=attribute/>" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}" class="${properties.kcInputClass!}"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
|
<#if attribute.readOnly>disabled</#if>
|
||||||
|
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypePlaceholder??>placeholder="${attribute.annotations.inputTypePlaceholder}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypePattern??>pattern="${attribute.annotations.inputTypePattern}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeSize??>size="${attribute.annotations.inputTypeSize}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeMaxlength??>maxlength="${attribute.annotations.inputTypeMaxlength}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeMinlength??>minlength="${attribute.annotations.inputTypeMinlength}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeMax??>max="${attribute.annotations.inputTypeMax}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeMin??>min="${attribute.annotations.inputTypeMin}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
|
||||||
|
/>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro inputTagType attribute>
|
||||||
|
<#compress>
|
||||||
|
<#if attribute.annotations.inputType??>
|
||||||
|
<#if attribute.annotations.inputType?starts_with("html5-")>
|
||||||
|
${attribute.annotations.inputType[6..]}
|
||||||
|
<#else>
|
||||||
|
${attribute.annotations.inputType}
|
||||||
|
</#if>
|
||||||
|
<#else>
|
||||||
|
text
|
||||||
|
</#if>
|
||||||
|
</#compress>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro textareaTag attribute>
|
||||||
|
<textarea id="${attribute.name}" name="${attribute.name}" class="${properties.kcInputClass!}"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
|
<#if attribute.readOnly>disabled</#if>
|
||||||
|
<#if attribute.annotations.inputTypeCols??>cols="${attribute.annotations.inputTypeCols}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeRows??>rows="${attribute.annotations.inputTypeRows}"</#if>
|
||||||
|
<#if attribute.annotations.inputTypeMaxlength??>maxlength="${attribute.annotations.inputTypeMaxlength}"</#if>
|
||||||
|
>${(attribute.value!'')}</textarea>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro selectTag attribute>
|
||||||
|
<select id="${attribute.name}" name="${attribute.name}" class="${properties.kcInputClass!}"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
|
<#if attribute.readOnly>disabled</#if>
|
||||||
|
<#if attribute.annotations.inputType=='multiselect'>multiple</#if>
|
||||||
|
<#if attribute.annotations.inputTypeSize??>size="${attribute.annotations.inputTypeSize}"</#if>
|
||||||
|
>
|
||||||
|
<#if attribute.annotations.inputType=='select'>
|
||||||
|
<option value=""></option>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??>
|
||||||
|
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
||||||
|
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
||||||
|
<#assign options=attribute.validators.options.options>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if options??>
|
||||||
|
<#list options as option>
|
||||||
|
<option value="${option}" <#if attribute.values?seq_contains(option)>selected</#if>><@selectOptionLabelText attribute=attribute option=option/></option>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</select>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro inputTagSelects attribute>
|
||||||
|
<#if attribute.annotations.inputType=='select-radiobuttons'>
|
||||||
|
<#assign inputType='radio'>
|
||||||
|
<#assign classDiv=properties.kcInputClassRadio!>
|
||||||
|
<#assign classInput=properties.kcInputClassRadioInput!>
|
||||||
|
<#assign classLabel=properties.kcInputClassRadioLabel!>
|
||||||
|
<#else>
|
||||||
|
<#assign inputType='checkbox'>
|
||||||
|
<#assign classDiv=properties.kcInputClassCheckbox!>
|
||||||
|
<#assign classInput=properties.kcInputClassCheckboxInput!>
|
||||||
|
<#assign classLabel=properties.kcInputClassCheckboxLabel!>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??>
|
||||||
|
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
|
||||||
|
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
|
||||||
|
<#assign options=attribute.validators.options.options>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if options??>
|
||||||
|
<#list options as option>
|
||||||
|
<div class="${classDiv}">
|
||||||
|
<input type="${inputType}" id="${attribute.name}-${option}" name="${attribute.name}" value="${option}" class="${classInput}"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
|
||||||
|
<#if attribute.readOnly>disabled</#if>
|
||||||
|
<#if attribute.values?seq_contains(option)>checked</#if>
|
||||||
|
/>
|
||||||
|
<label for="${attribute.name}-${option}" class="${classLabel}<#if attribute.readOnly> ${properties.kcInputClassRadioCheckboxLabelDisabled!}</#if>"><@selectOptionLabelText attribute=attribute option=option/></label>
|
||||||
|
</div>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</select>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
<#macro selectOptionLabelText attribute option>
|
||||||
|
<#compress>
|
||||||
|
<#if attribute.annotations.inputOptionLabels??>
|
||||||
|
${advancedMsg(attribute.annotations.inputOptionLabels[option]!option)}
|
||||||
|
<#else>
|
||||||
|
<#if attribute.annotations.inputOptionLabelsI18nPrefix??>
|
||||||
|
${msg(attribute.annotations.inputOptionLabelsI18nPrefix + '.' + option)}
|
||||||
|
<#else>
|
||||||
|
${option}
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</#compress>
|
||||||
|
</#macro>
|
168
login/webauthn-authenticate.ftl
Normal file
168
login/webauthn-authenticate.ftl
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "title">
|
||||||
|
title
|
||||||
|
<#elseif section = "header">
|
||||||
|
${kcSanitize(msg("webauthn-login-title"))?no_esc}
|
||||||
|
<#elseif section = "form">
|
||||||
|
<div id="kc-form-webauthn" class="${properties.kcFormClass!}">
|
||||||
|
<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("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>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
|
||||||
|
<span id="kc-webauthn-authenticator-created-label">
|
||||||
|
${kcSanitize(msg('webauthn-createdAt-label'))?no_esc}
|
||||||
|
</span>
|
||||||
|
<span id="kc-webauthn-authenticator-created">
|
||||||
|
${kcSanitize(authenticator.createdAt)?no_esc}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
|
||||||
|
</div>
|
||||||
|
</#list>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="${url.resourcesPath}/js/base64url.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function webAuthnAuthenticate() {
|
||||||
|
let isUserIdentified = ${isUserIdentified};
|
||||||
|
if (!isUserIdentified) {
|
||||||
|
doAuthenticate([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkAllowCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAllowCredentials() {
|
||||||
|
let allowCredentials = [];
|
||||||
|
let authn_use = document.forms['authn_select'].authn_use_chk;
|
||||||
|
|
||||||
|
if (authn_use !== undefined) {
|
||||||
|
if (authn_use.length === undefined) {
|
||||||
|
allowCredentials.push({
|
||||||
|
id: base64url.decode(authn_use.value, {loose: true}),
|
||||||
|
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>
|
36
login/webauthn-error.ftl
Normal file
36
login/webauthn-error.ftl
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=true; section>
|
||||||
|
<#if section = "header">
|
||||||
|
${kcSanitize(msg("webauthn-error-title"))?no_esc}
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
refreshPage = () => {
|
||||||
|
document.getElementById('isSetRetry').value = 'retry';
|
||||||
|
document.getElementById('executionValue').value = '${execution}';
|
||||||
|
document.getElementById('kc-error-credential-form').submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form id="kc-error-credential-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
|
||||||
|
method="post">
|
||||||
|
<input type="hidden" id="executionValue" name="authenticationExecution"/>
|
||||||
|
<input type="hidden" id="isSetRetry" name="isSetRetry"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<input tabindex="4" onclick="refreshPage()" type="button"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
name="try-again" id="kc-try-again" value="${kcSanitize(msg("doTryAgain"))?no_esc}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-webauthn-settings-form" method="post">
|
||||||
|
<button type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="cancelWebAuthnAIA" name="cancel-aia" value="true">${msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
197
login/webauthn-register.ftl
Normal file
197
login/webauthn-register.ftl
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout; section>
|
||||||
|
<#if section = "title">
|
||||||
|
title
|
||||||
|
<#elseif section = "header">
|
||||||
|
<span class="${properties.kcWebAuthnKeyIcon}"></span>
|
||||||
|
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
|
||||||
|
<#elseif section = "form">
|
||||||
|
|
||||||
|
<form id="register" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
|
||||||
|
<input type="hidden" id="attestationObject" name="attestationObject"/>
|
||||||
|
<input type="hidden" id="publicKeyCredentialId" name="publicKeyCredentialId"/>
|
||||||
|
<input type="hidden" id="authenticatorLabel" name="authenticatorLabel"/>
|
||||||
|
<input type="hidden" id="transports" name="transports"/>
|
||||||
|
<input type="hidden" id="error" name="error"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="${url.resourcesPath}/js/base64url.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function registerSecurityKey() {
|
||||||
|
|
||||||
|
// Check if WebAuthn is supported by this browser
|
||||||
|
if (!window.PublicKeyCredential) {
|
||||||
|
document.getElementById('error').value = '${msg("webauthn-unsupported-browser-text")?no_esc}';
|
||||||
|
document.getElementById('register').submit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mandatory parameters
|
||||||
|
let challenge = "${challenge}";
|
||||||
|
let userid = "${userid}";
|
||||||
|
let username = "${username}";
|
||||||
|
|
||||||
|
let signatureAlgorithms = "${signatureAlgorithms}";
|
||||||
|
let pubKeyCredParams = getPubKeyCredParams(signatureAlgorithms);
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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(signatureAlgorithms) {
|
||||||
|
let pubKeyCredParams = [];
|
||||||
|
if (signatureAlgorithms === "") {
|
||||||
|
pubKeyCredParams.push({type: "public-key", alg: -7});
|
||||||
|
return pubKeyCredParams;
|
||||||
|
}
|
||||||
|
let signatureAlgorithmsList = signatureAlgorithms.split(',');
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="registerWebAuthn"
|
||||||
|
onclick="registerSecurityKey()"
|
||||||
|
>${msg("doRegister")}</button>
|
||||||
|
|
||||||
|
<#if !isSetRetry?has_content && isAppInitiatedAction?has_content>
|
||||||
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-webauthn-settings-form"
|
||||||
|
method="post">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||||
|
id="cancelWebAuthnAIA"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>${msg("doCancel")}</button>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
Loading…
Reference in a new issue