lukin / keywind Goto Github PK
View Code? Open in Web Editor NEWKeywind is a component-based Keycloak Login Theme built with Tailwind CSS
License: Apache License 2.0
Keywind is a component-based Keycloak Login Theme built with Tailwind CSS
License: Apache License 2.0
Hi, was actually just wondering how you did this - is the tailwind not part of the build process when your doing keycloak, and you just build the components with tailwind and then just deploy it like that, or can you actually include tailwind in a keycloak theme and build the css etc?
Just wondering if this theme is set up with all the tailwind classes and the right css, but you couldn't add a class etc? Just wondering how it works! thanks
Hey Anthony,
I am currently playing with a Passwordless WebAuthn login. If I only display the user name in the Authentication Flow in the first step, the layout is destroyed and it no longer seems to load the CSS correctly.
I have already gone through the logs and can't find an error anywhere... I assume that the password form is stored somewhere in login.ftl as binding? (Edit: Ok, I think that other FTL files such as "webauthn-authenticate.ftl" still need to be styled?)
With the integrated theme it looks like this:
My Authentication Flow looks like this:
Best regards and thanks for your great work here!
Patrick
Hi @lukin,
I would suggest, to have an issue/discussion etc. to document the latest commit of the github-repo of changes to the email/login theme to be able to add them into this repo accordingly.
I've started to look into the changes within kc within the last year and had filed eigher an issue or created an PR for the change.
The latest commit checked on commit history of the themes-folder was on Sep 14, 2023
Hello,
Would be great to add a CI pipeline to build a JAR file also
I have seen the PR #32 whichi include build, but not the JAR gen
Thansk for your work !
The cancel button at the change password screen does nothing when fields are empty (not validating).
In Firefox, it appears to be a no-op. Safari shows "Fill out this field" hint when clicking cancel. Interestingly, passwords don't need to match in order to pass "validation" for purposes of cancelling.
Behavior is as expected in all browsers using default theme.
Keycloak version: 21.1.2
Keywind version: 6e5ef06
Hey there!
I'm using x509 certificate based authentication. The theme does not really look right for that at the moment:
It should look somewhat like this:
The base theme file is this one:
https://github.com/keycloak/keycloak/blob/a6ae1b9db7fa9f3517790f103be7411629cae4dc/themes/src/main/resources/theme/base/login/login-x509-info.ftl
I'll try to fix this myself and create a pull request if I succeed :)
Just a slight suggestion:
Add a show/hide password option to the password input.
I might take that at some point but i'm not really familiar with the templating lib.
when using update email, the next page is also keycloak default, maybe related to the missing userProfile implementation #45
Thank you so much for the amazing work!
Do you have any plans to add OTP login pages (configuration, one time code, etc.) in the near future?
Hi Anthony,
Thanks for sharing this project. I was trying to add a template for the account page and thought of sharing the components across login and account. Keycloak throws "Template not found for name ..." exception if I move the components directory under the keywind directory and make reference changes in the import URLs. I was wondering if it is possible to achieve this.
PS: I don't have much experience in Java and have not used Freemarker.
Hi ,
I was able to run "npm build" , copy that JAR file to my keycloak and add it to my keycloak.
But how can I run it Locally this login page of yours like on localhost:3000 and edit it additionally as I would like, so then I can build it again and use it for my keycloak.
On Keycloak Version 22.0.5:
Would you be able to fix it or help me figure out how to fix it :) ?
Here are the trace logs if they help!
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: 2023-11-02 14:00:35,873 SEVERE [freemarker.runtime] (executor-thread-6) Error executing FreeMarker template: freemarker.core.NonStringOrTemplateOutputException: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a sequence (wrapper: f.t.SimpleSequence):
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: ==> signatureAlgorithms [in template "webauthn-register.ftl" at line 47, column 31]
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: ----
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: FTL stack trace ("~" means nesting-related):
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011- Failed at: ${signatureAlgorithms} [in template "webauthn-register.ftl" at line 47, column 29]
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: ----
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at freemarker.core.EvalUtil.coerceModelToTextualCommon(EvalUtil.java:525)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:401)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:370)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:104)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at freemarker.core.DollarVariable.accept(DollarVariable.java:63)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: 2023-11-02 14:00:35,879 ERROR [org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider] (executor-thread-6) Failed to process template: org.keycloak.theme.FreeMarkerException: Failed to process template webauthn-register.ftl
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.theme.freemarker.DefaultFreeMarkerProvider.processTemplate(DefaultFreeMarkerProvider.java:52)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.processTemplate(FreeMarkerLoginFormsProvider.java:549)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.createForm(FreeMarkerLoginFormsProvider.java:342)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.authentication.requiredactions.WebAuthnRegister.requiredActionChallenge(WebAuthnRegister.java:165)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.services.managers.AuthenticationManager.executeAction(AuthenticationManager.java:1335)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.services.managers.AuthenticationManager.lambda$executionActions$18(AuthenticationManager.java:1282)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:400)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: 2023-11-02 14:00:35,883 ERROR [org.keycloak.headers.DefaultSecurityHeadersProvider] (executor-thread-6) MediaType not set on path /realms/master/login-actions/required-action, with response status 500
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: 2023-11-02 14:00:35,883 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-6) Uncaught server error: jakarta.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.headers.DefaultSecurityHeadersProvider.addHeaders(DefaultSecurityHeadersProvider.java:75)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.services.filters.KeycloakSecurityHeadersFilter.filter(KeycloakSecurityHeadersFilter.java:43)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:329)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:243)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:100)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:73)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:518)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:458)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:229)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:82)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:147)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:84)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:44)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:58)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:36)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$0(QuarkusRequestFilter.java:82)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
Nov 2 14:00:35 techinc-keycloak kc.sh[3817775]: #011at java.base/java.lang.Thread.run(Thread.java:833)
Manage account
Signing in
(Account security)Set up authenticator application
Mobile Authenticator Setup page
==> totp.policy.supportedApplications [in template "login-config-totp.ftl" at line 22, column 18]
----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: #list totp.policy.supportedApplicatio... [in template "login-config-totp.ftl" at line 22, column 11]
~ Reached through: #nested "form" [in template "template.ftl" in macro "registrationLayout" at line 44, column 13]
~ Reached through: #nested [in template "components/layout/card-main.ftl" in macro "kw" at line 3, column 5]
~ Reached through: @cardMain.kw [in template "template.ftl" in macro "registrationLayout" at line 40, column 11]
~ Reached through: #nested [in template "components/layout/card.ftl" in macro "kw" at line 3, column 5]
~ Reached through: @card.kw [in template "template.ftl" in macro "registrationLayout" at line 29, column 9]
~ Reached through: #nested [in template "components/layout/container.ftl" in macro "kw" at line 4, column 7]
~ Reached through: @container.kw [in template "template.ftl" in macro "registrationLayout" at line 28, column 7]
- Reached through: @layout.registrationLayout displayMes... [in template "login-config-totp.ftl" at line 9, column 1]
----
at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134)
at freemarker.core.Expression.assertNonNull(Expression.java:249)
at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:104)
at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94)
at freemarker.core.Environment.visit(Environment.java:347)
at freemarker.core.Environment.visit(Environment.java:389)
at freemarker.core.Environment.invokeNestedContent(Environment.java:633)
at freemarker.core.BodyInstruction.accept(BodyInstruction.java:60)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeNestedContent(Environment.java:633)
at freemarker.core.BodyInstruction.accept(BodyInstruction.java:60)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
at freemarker.core.Environment.invokeMacro(Environment.java:825)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeNestedContent(Environment.java:633)
at freemarker.core.BodyInstruction.accept(BodyInstruction.java:60)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
at freemarker.core.Environment.invokeMacro(Environment.java:825)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeNestedContent(Environment.java:633)
at freemarker.core.BodyInstruction.accept(BodyInstruction.java:60)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
at freemarker.core.Environment.invokeMacro(Environment.java:825)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
at freemarker.core.Environment.visit(Environment.java:383)
at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
at freemarker.core.Environment.invokeMacro(Environment.java:825)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
at freemarker.core.Environment.visit(Environment.java:347)
at freemarker.core.Environment.visit(Environment.java:353)
at freemarker.core.Environment.process(Environment.java:326)
at freemarker.template.Template.process(Template.java:383)
at org.keycloak.theme.freemarker.DefaultFreeMarkerProvider.processTemplate(DefaultFreeMarkerProvider.java:49)
... 56 more
2022-11-02 19:55:11,830 ERROR [org.keycloak.headers.DefaultSecurityHeadersProvider] (executor-thread-38) MediaType not set on path /realms/master/login-actions/required-action, with response status 500
2022-11-02 19:55:11,831 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-38) Uncaught server error: javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
at org.keycloak.headers.DefaultSecurityHeadersProvider.addHeaders(DefaultSecurityHeadersProvider.java:75)
at org.keycloak.services.filters.KeycloakSecurityHeadersFilter.filter(KeycloakSecurityHeadersFilter.java:42)
at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:367)
at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:252)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:101)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:74)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:594)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:84)
at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:71)
at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:430)
at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:408)
at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$0(QuarkusRequestFilter.java:82)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:564)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:829)```
Hey volks,
are there plans to introduce an keycloak tailwind / mailwind email theme?
FTL stack trace ("~" means nesting-related):
- Failed at: #list totp.supportedApplications as app [in template "login-config-totp.ftl" at line 26, column 11]
~ Reached through: #nested "form" [in template "template.ftl" in macro "registrationLayout" at line 45, column 5]
~ Reached through: @layout.registrationLayout displayMes... [in template "login-config-totp.ftl" at line 13, column " at line 26, column 11]
~ Reached through: #nested "form" [in template "template.ftl" in macro "registrationLayout" at line 45, column 5]
~ Reached through: @layout.registrationLayout displayMes... [in template "login-config-totp.ftl" at line 13, column
How does one centre the logo image on the screen? It seems to be left aligned by default. This is my logo.ftl:
<#macro kw>
<div class="font-bold text-center text-2xl">
<img src="${url.resourcesPath}/logo_light.svg" class="logo">
<#nested>
</div>
</#macro>
I tried adjusting my svg but it starts to look weird on different sized screens so I guess there is a better way ๐
What is the right way to add images, whether to be referenced as an image tag or in CSS? For example, as a background image in the case of CSS or a logo in the page.
For the background I am looking at the documentation, but I am still a little confused, being a tailwind newbie. I suspect the path would be ../images/myimage.png
if the image is in a sister folder to the CSS, but I am still not sure on the right entry.
For an img
tag I've worked around it by putting the image on another server as accessing by an absolute URL, but ideally I'd like the image to be local to the theme. I've looked at the Keycloak documentation, I am still confused.
Edit: for the last one, it would seem to be (see here), but will need to test later:
<img src="${url.resourcesPath}/img/sample.png" />
Hey there. Can you guide me on how to use Keywind in development mode? Any help will be appreciated.
related change in keycloak:
keycloak/keycloak@ee35cfe#diff-fd535979f585db34c97e90dcda60c34f7b7e0284343fd4d2934b920364ded702
Hi All, Really liking this theme, Looks Good :)
Tried to add WebAuthn to a realm just now tho and I get an error when it get to the 'enroll' step.
I also get the following in the logs:
`FTL stack trace ("~" means nesting-related):
- Failed at: ${properties.kcWebAuthnKeyIcon} [in template "webauthn-register.ftl" at line 6, column 22]
- Reached through: @layout.registrationLayout; section [in template "webauthn-register.ftl" at line 2, column 5]
(Hidden 9 "~" lines for terseness)`
I'm not sure if this happens when keys have been added already but as I cant seem to see any files with 'webauthn' in the name in this git repo I don't think its been added.
Is somebody looking at adding support for webauthn? I can try but it will be the first time I have played with a keycloak theme so might not be very good.
Thanks,
Matt.
The locale component doesn't have display none on click, it's always displayed.
This is the error that appears on the console:
Uncaught TypeError: i(...).catch is not a function
at alpinejs.js:1707
at jn (alpinejs.js:1634)
at alpinejs.js:3349
at a (alpinejs.js:2767)
at alpinejs.js:2805
at alpinejs.js:2769
at alpinejs.js:2814
at HTMLDocument.<anonymous> (alpinejs.js:2769)
Alpine Expression Error: i(...).catch is not a function
Expression: "open = false"
<div class=โ"absolute bg-white bottom-0 -left-4 max-h-80 mb-6 overflow-y-scroll rounded-lg shadow-lg" x-show=โ"open" @click.away=โ"open = false">โโฆโ</div>โ
Le @ alpinejs.js:1641
jn @ alpinejs.js:1636
(anonymous) @ alpinejs.js:3349
a @ alpinejs.js:2767
(anonymous) @ alpinejs.js:2805
(anonymous) @ alpinejs.js:2769
(anonymous) @ alpinejs.js:2814
(anonymous) @ alpinejs.js:2769
I didn't change anything in the theme, I just pasted the theme into the keycloak themes folder.
Keywind is missing register-user-profile.ftl, so it will fallback to base-theme.
Orginal Post:
Within my deployment the register form is not displayed correctly:
The browser-console shows no class-names for the children.
Can someone reproduce this? I was also not able to fix this by looking into the files.
Every other site works just fine.
File:
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("firstName", "lastName", "email", "username", "password", "password-confirm")
;
section
>
<#if section="header">
${msg("registerTitle")}
<#elseif section="form">
<@form.kw action=url.registrationAction method="post">
<@input.kw
autocomplete="given-name"
autofocus=true
invalid=messagesPerField.existsError("firstName")
label=msg("firstName")
message=kcSanitize(messagesPerField.get("firstName"))
name="firstName"
type="text"
value=(register.formData.firstName)!''
/>
<@input.kw
autocomplete="family-name"
invalid=messagesPerField.existsError("lastName")
label=msg("lastName")
message=kcSanitize(messagesPerField.get("lastName"))
name="lastName"
type="text"
value=(register.formData.lastName)!''
/>
<@input.kw
autocomplete="email"
invalid=messagesPerField.existsError("email")
label=msg("email")
message=kcSanitize(messagesPerField.get("email"))
name="email"
type="email"
value=(register.formData.email)!''
/>
<#if !realm.registrationEmailAsUsername>
<@input.kw
autocomplete="username"
invalid=messagesPerField.existsError("username")
label=msg("username")
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(register.formData.username)!''
/>
</#if>
<#if passwordRequired??>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password", "password-confirm")
label=msg("password")
message=kcSanitize(messagesPerField.getFirstError("password", "password-confirm"))
name="password"
type="password"
/>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password-confirm")
label=msg("passwordConfirm")
message=kcSanitize(messagesPerField.get("password-confirm"))
name="password-confirm"
type="password"
/>
</#if>
<#if recaptchaRequired??>
<div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}" data-size="compact"></div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" type="submit">
${msg("doRegister")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
<#elseif section="nav">
<@link.kw color="secondary" href=url.loginUrl size="small">
${kcSanitize(msg("backToLogin"))?no_esc}
</@link.kw>
</#if>
</@layout.registrationLayout>
browser-html:
<div class="bg-white p-8 rounded-lg space-y-6">
<div class="space-y-4">
<div class="font-bold text-center text-2xl">
<div class="kc-logo-text"><span></span></div>
</div>
<h1 class="text-center text-xl">
Register
</h1>
</div>
<div class="space-y-4">
<form id="kc-register-form" class="" action="https://keycloak-instance-url/realms/master/login-actions/registration?session_code=gFZaf7Q4ZkBoTJkkc1a2FD2O7bfPQp73Ds4wzl_Uxw0&execution=35a0cff7-75be-4c9e-9a12-8af40885b954&client_id=security-admin-console&tab_id=8UEk3-Dq90E" method="post">
<div class="">
<div class="">
<label for="username" class="">Username</label>
*
</div>
<div class="">
<input type="text" id="username" name="username" value="" class="" aria-invalid="" autocomplete="username">
</div>
</div>
<div class="">
<div class="">
<label for="password" class="">Password</label> *
</div>
<div class="">
<input type="password" id="password" class="" name="password" autocomplete="new-password" aria-invalid="" aria-autocomplete="list">
</div>
</div>
<div class="">
<div class="">
<label for="password-confirm" class="">Confirm password</label> *
</div>
<div class="">
<input type="password" id="password-confirm" class="" name="password-confirm" aria-invalid="">
</div>
</div>
<div class="">
<div class="">
<label for="email" class="">Email</label>
*
</div>
<div class="">
<input type="text" id="email" name="email" value="" class="" aria-invalid="" autocomplete="email">
</div>
</div>
<div class="">
<div class="">
<label for="firstName" class="">First name</label>
*
</div>
<div class="">
<input type="text" id="firstName" name="firstName" value="" class="" aria-invalid="">
</div>
</div>
<div class="">
<div class="">
<label for="lastName" class="">Last name</label>
*
</div>
<div class="">
<input type="text" id="lastName" name="lastName" value="" class="" aria-invalid="">
</div>
</div>
<div class="">
<div id="kc-form-options" class="">
<div class="">
<span><a href="/realms/master/login-actions/authenticate?client_id=security-admin-console&tab_id=8UEk3-Dq90E">ยซ Back to Login</a></span>
</div>
</div>
<div id="kc-form-buttons" class="">
<input class=" " type="submit" value="Register">
</div>
</div>
</form>
<p class="text-secondary-600 text-sm">
* Required fields
</p>
</div>
</div>
Thank you for your effort with this theme!
I found a small issue, and that is that email validation triggers even for usernames that are not emails.
Set Email as username
to false for some realm
Create a user with a non-email username, eg: myuser
Try to login, which will not be possible due to the validation firing
Note: This can cause you to be locked out from keycloak if you choose keywind as a theme for master realm and have an admin user without an email-like username.
Cheers!
To respect RTL languages keywind has to use dir
HTML attribute
https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute
So the suggestion is to modify template.ftl
with following
<html <#if realm.internationalizationEnabled> lang="${locale.currentLanguageTag}"</#if> dir=auto>
Hey ๐
I need your help! I would really love to hear what you would like to see in Keywind.
Features:
Pages:
Hi there, thanks for your work.
I got this error when use the theme without any change:
Caused by: freemarker.core.NonMethodException: Expected a method, but this has evaluated to a string (wrapper: f.t.SimpleScalar):
==> messagesPerField.existsError [in template "login.ftl" at line 11, column 19]
----
FTL stack trace ("~" means nesting-related):
- Failed at: #macro registrationLayout displayInfo... [in template "template.ftl" in macro "registrationLayout" at line 16, column 1]
- Reached through: @layout.registrationLayout displayInf... [in template "login.ftl" at line 9, column 1]
----
Any idea?
We use: docker.io/jboss/keycloak:8.0.1
Olivier.
Hello !
I recently updated to Keycloak 22 (more precisely 22.0.1, was using version 21 before), and I think I spotted a bug on WebAuthn security key registration.
Caused by: freemarker.core.NonStringOrTemplateOutputException: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a sequence (wrapper: f.t.SimpleSequence):
==> signatureAlgorithms [in template "webauthn-register.ftl" at line 47, column 31]
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${signatureAlgorithms} [in template "webauthn-register.ftl" at line 47, column 29]
----
Could this be related to #20831 or am I missing something ?
keycloak related commit:
keycloak/keycloak@041441f#diff-3ebf55840337734bc375264421e082977c1ec715f041218fbaecb5fc2612dad3
TODO:
should be styled
Dear all,
I try latest keywind source code with google recaptcha v2 (checkbox). I try this video tutorial https://www.youtube.com/watch?v=YfHfRGiqZEU
Currently, if I use theme keycloak, it works as expectation. When I change to keywind login theme, it does not show anything in recaptcha. Height of recaptcha div is 0 (debug in Chrome)
Thanks for help
Just discovered this theme. Very nice.
In Keycloak 22.0.0 they added the option for terms and conditions on the registry page. See keycloak/keycloak#7938.
Would be nice if the theme supported that also. Thanks.
I've been using this repository as a starter to customize a Keycloak theme with tailwind and it has been awesome in getting off the ground fast. However, one issue I've been having is viewing the themes of pages that are not sign in/sign up/forgot password.
So far I've just been mounting the themes folder to my local Keycloak docker compose deployment and accessing the pages through Keycloak itself. However, i have not found a way to view all the pages. Is there another way to preview pages or is there an option in Keycloak to see the pages?
I saw there is a html directory in this repo but it looks like its completely separate from the theme itself. Is there a way to utilize these perhaps?
Keycloak received a new page to confirm a logout. Would be great to have it in keywind ๐
If you add the property favicons, it breaks the template engine.
Looks like a typo in document.ftl:
<#if properties.favicons?has_content>
<#list properties.favicons?split(" ") as favicon>
<link href="${url.resourcesPath}/${favicon?split('==')[0]}" rel="${meta?split('==')[1]}">
</#list>
</#if>
rel should be "icon" not something in meta
This works:
<#if properties.favicons?has_content>
<#list properties.favicons?split(" ") as favicon>
<link href="${url.resourcesPath}/${favicon?split('==')[0]}" rel="icon">
</#list>
</#if>
Hi, I really like the keywind theme after all. However, there is a minor issue that it doesn't display "Invalid password: minimum length 10." correctly on registration.
I am not sure how to patch it.
I am looking to add a logo to the login page, but it is not clear where I should be defining client.attributes.logoUri
? Should this be in theme.properties?
I did read ticket #79 , I also see in login-oauth-grant.ftl
: <#if client.attributes.logoUri??>
or is this for a different purpose?
Currently renders a bit broken, with the buttons showing as unstyled input
nodes.
hi,
Thanks for wonderful Theme.
I want to change the keycloack title on top of my page, But I can't find where to change this.
Can you please give me some info on that?
First off, great job on the theme.
Given I've seen a number of issues around documentation, would you be open to creating a wiki for the project (maybe with an FAQ?) or creating a discussions section?
Hello,
I seem to be getting an error when using SAML V2.0 with your theme applied, my best guess is that it doesn't have the icon it needs to render.
It would be good to see in readme information of supported keycloak versions. It will help understand, do I have a problem with my configuration or version not supported.
Hi,
I just saw that a html
folder has been added together with the testing capabilities.
Is the only purpose of the html
folder to run tests on it or is there any other purpose for having the plain html?
If so I could offer to create a Pull Request stating this circumstance.
Hey folks!
First of all I have to say it was super easy to update the login pages! I see a login-update-profile.ftl page and I want to use the same for the personal info page in account management found at this URL: http://HOST:8080/realms/realmname/account/
I have created a new account folder and copied over the following from the login folder:
How can I reuse the login-update-profile.ftl file as my personal info page to allow the user to update their account information with the same theme as the login pages?
Hi!
Since this commit, the logout is not working anymore:
Do you have an idea where does it come from?
I was getting compilation issues on the above line for keycloak 23.0.3, made the adjustments to
signatureAlgorithms: '<#list signatureAlgorithms as sigAlg>${sigAlg}<#sep>,</#list>'
and seemed to be resolved.
Changes needed according to:
keycloak/keycloak@86c0e33
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.