From 4bcff2c1e73f69fcc46aa525751216d20dc8401f Mon Sep 17 00:00:00 2001 From: Sofia Papacharalampous Date: Tue, 25 Jun 2024 14:45:40 +0300 Subject: [PATCH] keycloak login page customization --- keycloak-themes/login-theme/login/login.ftl | 115 ++++++++++++ .../login/resources/css/styles.css | 67 +++++++ .../login/resources/img/nav-logo.png | Bin 0 -> 4567 bytes .../login-theme/login/template.ftl | 173 ++++++++++++++++++ .../login-theme/login/theme.properties | 12 ++ keycloak-themes/readme.txt | 2 + 6 files changed, 369 insertions(+) create mode 100644 keycloak-themes/login-theme/login/login.ftl create mode 100644 keycloak-themes/login-theme/login/resources/css/styles.css create mode 100644 keycloak-themes/login-theme/login/resources/img/nav-logo.png create mode 100644 keycloak-themes/login-theme/login/template.ftl create mode 100644 keycloak-themes/login-theme/login/theme.properties create mode 100644 keycloak-themes/readme.txt diff --git a/keycloak-themes/login-theme/login/login.ftl b/keycloak-themes/login-theme/login/login.ftl new file mode 100644 index 000000000..81c9dbf14 --- /dev/null +++ b/keycloak-themes/login-theme/login/login.ftl @@ -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"> +
+
+ <#if realm.password> +
+ <#if !usernameHidden??> +
+ + + + + <#if messagesPerField.existsError('username','password')> + + ${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc} + + + +
+ + +
+ + +
+ + +
+ + <#if usernameHidden?? && messagesPerField.existsError('username','password')> + + ${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc} + + + +
+ +
+
+ <#if realm.rememberMe && !usernameHidden??> +
+ +
+ +
+
+ <#if realm.resetPasswordAllowed> + ${msg("doForgotPassword")} + +
+ +
+ +
+ value="${auth.selectedCredential}"/> + +
+
+ +
+
+ + <#elseif section = "info" > + <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+
+ ${msg("noAccount")} ${msg("doRegister")} +
+
+ + <#elseif section = "socialProviders" > + <#if realm.password && social.providers??> +
+
+

${msg("identity-provider-login-label")}

+ + +
+ + + + diff --git a/keycloak-themes/login-theme/login/resources/css/styles.css b/keycloak-themes/login-theme/login/resources/css/styles.css new file mode 100644 index 000000000..55200145e --- /dev/null +++ b/keycloak-themes/login-theme/login/resources/css/styles.css @@ -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; +} diff --git a/keycloak-themes/login-theme/login/resources/img/nav-logo.png b/keycloak-themes/login-theme/login/resources/img/nav-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9be9fcdabfea9c532b94229c7c41121828c39f92 GIT binary patch literal 4567 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 + + lang="${locale.currentLanguageTag}"> + + + + + + + <#if properties.meta?has_content> + <#list properties.meta?split(' ') as meta> + + + + ${msg("loginTitle",(realm.displayName!''))} + + <#if properties.stylesCommon?has_content> + <#list properties.stylesCommon?split(' ') as style> + + + + <#if properties.styles?has_content> + <#list properties.styles?split(' ') as style> + + + + <#if properties.scripts?has_content> + <#list properties.scripts?split(' ') as script> + + + + <#if scripts??> + <#list scripts as script> + + + + <#if authenticationSession??> + + + + + + +
+
+
+
+
+ +
+
+
${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}
+
+
+
+ <#if realm.internationalizationEnabled && locale.supported?size gt 1> +
+
+
+ ${locale.current} +
    + <#list locale.supported as l> +
  • + ${l.label} +
  • + +
+
+
+
+ + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <#if displayRequiredFields> +
+
+ * ${msg("requiredFields")} +
+
+

<#nested "header">

+
+
+ <#else> +

<#nested "header">

+ + <#else> + <#if displayRequiredFields> +
+
+ * ${msg("requiredFields")} +
+
+ <#nested "show-username"> +
+ + + + +
+
+
+ <#else> + <#nested "show-username"> +
+ + + + +
+ + +
+
+
+ + <#-- 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??)> +
+
+ <#if message.type = 'success'> + <#if message.type = 'warning'> + <#if message.type = 'error'> + <#if message.type = 'info'> +
+ ${kcSanitize(message.summary)?no_esc} +
+ + + <#nested "form"> + + <#if auth?has_content && auth.showTryAnotherWayLink()> +
+ +
+ + + <#nested "socialProviders"> + + <#if displayInfo> +
+
+ <#nested "info"> +
+
+ +
+
+ +
+
+ + + + diff --git a/keycloak-themes/login-theme/login/theme.properties b/keycloak-themes/login-theme/login/theme.properties new file mode 100644 index 000000000..378f2f435 --- /dev/null +++ b/keycloak-themes/login-theme/login/theme.properties @@ -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 diff --git a/keycloak-themes/readme.txt b/keycloak-themes/readme.txt new file mode 100644 index 000000000..bc7c2ddf3 --- /dev/null +++ b/keycloak-themes/readme.txt @@ -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 \ No newline at end of file