FTL templates in theme-resources/templates/ not loading in Keycloak-x

Hi All,

I think I found a bug in Keycloak-x ftl template loading. Posting here to see if anyone is seeing the same behavior. This is a description of an extension that works fine in Keycloak.

  1. Place an .ftl template in the src/main/resorces/theme-resources/templates/ dir of any extension
  2. Create an Authenticator or custom REST endpoint that serves the template, like:
  ...
  return session.getProvider(LoginFormsProvider.class).createForm("foo.ftl");
}
...
  1. Deploy the extension to the providers/ dir of Keycloak-x (standard distribution or docker)
  2. Get the following error, where the template cannot be found:
2022-02-07 20:06:27,898 ERROR [org.key.for.log.fre.FreeMarkerLoginFormsProvider] (executor-thread-0) Failed to process template: org.keycloak.theme.FreeMarkerException: Failed to process template foo.ftl
	at org.keycloak.theme.FreeMarkerUtil.processTemplate(FreeMarkerUtil.java:71)
	at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.processTemplate(FreeMarkerLoginFormsProvider.java:512)
	at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.createForm(FreeMarkerLoginFormsProvider.java:302)
	at com.example.FooResourceProvider.foo(FooResourceProvider.java:63)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
	at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
	at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
	at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
	at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
	at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
	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:135)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:81)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:41)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1193)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
	at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1193)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
	at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:362)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:340)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1193)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
	at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
	at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:66)
	at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
	at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
	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)
Caused by: freemarker.template.TemplateNotFoundException: Template not found for name "foo.ftl".
The name was interpreted by this TemplateLoader: org.keycloak.theme.FreeMarkerUtil$ThemeTemplateLoader@1e633f79.
	at freemarker.template.Configuration.getTemplate(Configuration.java:2883)
	at freemarker.template.Configuration.getTemplate(Configuration.java:2703)
	at org.keycloak.theme.FreeMarkerUtil.getTemplate(FreeMarkerUtil.java:85)
	at org.keycloak.theme.FreeMarkerUtil.processTemplate(FreeMarkerUtil.java:64)
	... 52 more

Does anyone have similar problems?

Thanks!

This is indeed broken in the new 17.0.0 Quarkus distribution of Keycloak. In order to use theme-resources in extensions, you must use a workaround like this:

Thanks @thomasdarimont !

Hm… :thinking:
Can‘t repdroduce the error…

I‘m using a custom authenticator with custom .ftl file with my dasniko/keycloak-2fa-sms-authenticator. When I deploy this to recent KC17, it works ootb. The template is found and displayed.

What do I do wrong for not getting the error?

BTW: Your linked file is no more available, Thomas just removed it…!

Hm. Maybe a bugfix made it into 17. I’ll check to see why it wasn’t working with the pre-release but it’s working now.

1 Like