keycloak login page customization
This commit is contained in:
parent
6204b5b3e7
commit
4bcff2c1e7
|
@ -0,0 +1,115 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') 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" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<#if messagesPerField.existsError('username','password')>
|
||||||
|
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||||
|
|
||||||
|
<div class="${properties.kcInputGroup!}">
|
||||||
|
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="off"
|
||||||
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||||
|
/>
|
||||||
|
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg("showPassword")}"
|
||||||
|
aria-controls="password" data-password-toggle
|
||||||
|
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||||
|
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||||
|
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
||||||
|
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||||
|
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||||
|
</span>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||||
|
<div id="kc-form-options">
|
||||||
|
<#if realm.rememberMe && !usernameHidden??>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<#if login.rememberMe??>
|
||||||
|
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
|
||||||
|
<#else>
|
||||||
|
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
|
||||||
|
</#if>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||||
|
<#if realm.resetPasswordAllowed>
|
||||||
|
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||||
|
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
|
||||||
|
<input tabindex="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>
|
||||||
|
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||||
|
<#elseif section = "info" >
|
||||||
|
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||||
|
<div id="kc-registration-container">
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>${msg("noAccount")} <a tabindex="6"
|
||||||
|
href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
<#elseif section = "socialProviders" >
|
||||||
|
<#if realm.password && social.providers??>
|
||||||
|
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||||
|
<hr/>
|
||||||
|
<h4>${msg("identity-provider-login-label")}</h4>
|
||||||
|
|
||||||
|
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||||
|
<#list social.providers as p>
|
||||||
|
<li>
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</@layout.registrationLayout>
|
|
@ -0,0 +1,67 @@
|
||||||
|
:root {
|
||||||
|
--primary-color: #129d99;
|
||||||
|
--primary-color2: #23bcba;
|
||||||
|
--primary-color3: #00b29f;
|
||||||
|
--secondary-color: #f7dd72;
|
||||||
|
--logo: url(../img/nav-logo.png);
|
||||||
|
--font-size: 0.87rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-pf body {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
background: transparent none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-pf-page {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Card */
|
||||||
|
#kc-header {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-pf {
|
||||||
|
background: #cecece17;
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-c-button.pf-m-primary {
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
font-size: var(--font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
color: #000;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 0 6px #00000029;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menu */
|
||||||
|
.login-pf-page-menu {
|
||||||
|
min-height: 80px;
|
||||||
|
display: flex;
|
||||||
|
box-shadow: var(--pf-global--BoxShadow--lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.login-pf-page-menu {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-pf-page-menu-logo-container {
|
||||||
|
width: auto;
|
||||||
|
margin: 0.5rem auto 0.5rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-pf-page-menu-logo {
|
||||||
|
background: no-repeat center / contain var(--logo);
|
||||||
|
min-width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,173 @@
|
||||||
|
<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="${properties.kcHtmlClass!}"<#if realm.internationalizationEnabled> lang="${locale.currentLanguageTag}"</#if>>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
|
||||||
|
<#if properties.meta?has_content>
|
||||||
|
<#list properties.meta?split(' ') as meta>
|
||||||
|
<meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}"/>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<title>${msg("loginTitle",(realm.displayName!''))}</title>
|
||||||
|
<link rel="icon" href="${url.resourcesPath}/img/favicon.ico" />
|
||||||
|
<#if properties.stylesCommon?has_content>
|
||||||
|
<#list properties.stylesCommon?split(' ') as style>
|
||||||
|
<link href="${url.resourcesCommonPath}/${style}" rel="stylesheet" />
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<#if properties.styles?has_content>
|
||||||
|
<#list properties.styles?split(' ') as style>
|
||||||
|
<link href="${url.resourcesPath}/${style}" rel="stylesheet" />
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<#if properties.scripts?has_content>
|
||||||
|
<#list properties.scripts?split(' ') as script>
|
||||||
|
<script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<#if scripts??>
|
||||||
|
<#list scripts as script>
|
||||||
|
<script src="${script}" type="text/javascript"></script>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
<#if authenticationSession??>
|
||||||
|
<script type="module">
|
||||||
|
import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";
|
||||||
|
|
||||||
|
checkCookiesAndSetTimer(
|
||||||
|
"${authenticationSession.authSessionId}",
|
||||||
|
"${authenticationSession.tabId}",
|
||||||
|
"${url.ssoLoginInOtherTabsUrl}"
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</#if>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="${properties.kcBodyClass!}">
|
||||||
|
|
||||||
|
<div class="${properties.ckcLoginMenuClass!}">
|
||||||
|
<div class="${properties.ckcLoginMenuLogoContainerClass!}">
|
||||||
|
<div class="${properties.ckcLoginMenuLogoClass!}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="${properties.kcLoginClass!}">
|
||||||
|
<div id="kc-header" class="${properties.kcHeaderClass!}">
|
||||||
|
<div id="kc-header-wrapper"
|
||||||
|
class="${properties.kcHeaderWrapperClass!}">${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}</div>
|
||||||
|
</div>
|
||||||
|
<div class="${properties.kcFormCardClass!}">
|
||||||
|
<header class="${properties.kcFormHeaderClass!}">
|
||||||
|
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
|
||||||
|
<div class="${properties.kcLocaleMainClass!}" id="kc-locale">
|
||||||
|
<div id="kc-locale-wrapper" class="${properties.kcLocaleWrapperClass!}">
|
||||||
|
<div id="kc-locale-dropdown" class="${properties.kcLocaleDropDownClass!}">
|
||||||
|
<a href="#" id="kc-current-locale-link">${locale.current}</a>
|
||||||
|
<ul class="${properties.kcLocaleListClass!}">
|
||||||
|
<#list locale.supported as l>
|
||||||
|
<li class="${properties.kcLocaleListItemClass!}">
|
||||||
|
<a class="${properties.kcLocaleItemClass!}" href="${l.url}">${l.label}</a>
|
||||||
|
</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
|
||||||
|
<#if displayRequiredFields>
|
||||||
|
<div class="${properties.kcContentWrapperClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!} subtitle">
|
||||||
|
<span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<h1 id="kc-page-title"><#nested "header"></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<#else>
|
||||||
|
<h1 id="kc-page-title"><#nested "header"></h1>
|
||||||
|
</#if>
|
||||||
|
<#else>
|
||||||
|
<#if displayRequiredFields>
|
||||||
|
<div class="${properties.kcContentWrapperClass!}">
|
||||||
|
<div class="${properties.kcLabelWrapperClass!} subtitle">
|
||||||
|
<span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<#nested "show-username">
|
||||||
|
<div id="kc-username" class="${properties.kcFormGroupClass!}">
|
||||||
|
<label id="kc-attempted-username">${auth.attemptedUsername}</label>
|
||||||
|
<a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">
|
||||||
|
<div class="kc-login-tooltip">
|
||||||
|
<i class="${properties.kcResetFlowIcon!}"></i>
|
||||||
|
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<#else>
|
||||||
|
<#nested "show-username">
|
||||||
|
<div id="kc-username" class="${properties.kcFormGroupClass!}">
|
||||||
|
<label id="kc-attempted-username">${auth.attemptedUsername}</label>
|
||||||
|
<a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">
|
||||||
|
<div class="kc-login-tooltip">
|
||||||
|
<i class="${properties.kcResetFlowIcon!}"></i>
|
||||||
|
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
</header>
|
||||||
|
<div id="kc-content">
|
||||||
|
<div id="kc-content-wrapper">
|
||||||
|
|
||||||
|
<#-- App-initiated actions should not see warning messages about the need to complete the action -->
|
||||||
|
<#-- during login. -->
|
||||||
|
<#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)>
|
||||||
|
<div class="alert-${message.type} ${properties.kcAlertClass!} pf-m-<#if message.type = 'error'>danger<#else>${message.type}</#if>">
|
||||||
|
<div class="pf-c-alert__icon">
|
||||||
|
<#if message.type = 'success'><span class="${properties.kcFeedbackSuccessIcon!}"></span></#if>
|
||||||
|
<#if message.type = 'warning'><span class="${properties.kcFeedbackWarningIcon!}"></span></#if>
|
||||||
|
<#if message.type = 'error'><span class="${properties.kcFeedbackErrorIcon!}"></span></#if>
|
||||||
|
<#if message.type = 'info'><span class="${properties.kcFeedbackInfoIcon!}"></span></#if>
|
||||||
|
</div>
|
||||||
|
<span class="${properties.kcAlertTitleClass!}">${kcSanitize(message.summary)?no_esc}</span>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#nested "form">
|
||||||
|
|
||||||
|
<#if auth?has_content && auth.showTryAnotherWayLink()>
|
||||||
|
<form id="kc-select-try-another-way-form" action="${url.loginAction}" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<input type="hidden" name="tryAnotherWay" value="on"/>
|
||||||
|
<a href="#" id="try-another-way"
|
||||||
|
onclick="document.forms['kc-select-try-another-way-form'].submit();return false;">${msg("doTryAnotherWay")}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#nested "socialProviders">
|
||||||
|
|
||||||
|
<#if displayInfo>
|
||||||
|
<div id="kc-info" class="${properties.kcSignUpClass!}">
|
||||||
|
<div id="kc-info-wrapper" class="${properties.kcInfoAreaWrapperClass!}">
|
||||||
|
<#nested "info">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</#macro>
|
|
@ -0,0 +1,12 @@
|
||||||
|
parent=keycloak
|
||||||
|
import=common/keycloak
|
||||||
|
styles=css/login.css css/styles.css
|
||||||
|
scripts=js/script.js
|
||||||
|
|
||||||
|
# custom keycloak classes
|
||||||
|
ckcLoginMenuClass=login-pf-page-menu
|
||||||
|
ckcLoginMenuLogoContainerClass=login-pf-page-menu-logo-container
|
||||||
|
ckcLoginMenuLogoClass=login-pf-page-menu-logo
|
||||||
|
|
||||||
|
# keycloak classes
|
||||||
|
kcBodyClass=login-pf-body
|
|
@ -0,0 +1,2 @@
|
||||||
|
create a JAR using these command: jar cvf login-theme.jar login-theme/
|
||||||
|
deploy the custom theme: https://www.keycloak.org/docs/latest/server_development/#deploying-themes
|
Loading…
Reference in New Issue