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.
Place an .ftl template in the src/main/resorces/theme-resources/templates/ dir of any extension
Create an Authenticator or custom REST endpoint that serves the template, like:
Deploy the extension to the providers/ dir of Keycloak-x (standard distribution or docker)
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
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:
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.
@dasniko I tried your sms authenticator and got the following result:
keycloak_1 | 2022-08-17 04:47:49,350 ERROR [org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider] (executor-thread-14) Failed to process template: org.keycloak.theme.FreeMarkerException: Failed to process template update-mobile-number.ftl
keycloak_1 | at org.keycloak.theme.FreeMarkerUtil.processTemplate(FreeMarkerUtil.java:71)
keycloak_1 | at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.processTemplate(FreeMarkerLoginFormsProvider.java:524)
keycloak_1 | at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.createForm(FreeMarkerLoginFormsProvider.java:326)
keycloak_1 | at dasniko.keycloak.requiredaction.MobileNumberRequiredAction.createForm(MobileNumberRequiredAction.java:77)
keycloak_1 | at dasniko.keycloak.requiredaction.MobileNumberRequiredAction.requiredActionChallenge(MobileNumberRequiredAction.java:39)
keycloak_1 | at org.keycloak.services.managers.AuthenticationManager.executeAction(AuthenticationManager.java:1310)
keycloak_1 | at org.keycloak.services.managers.AuthenticationManager.lambda$executionActions$18(AuthenticationManager.java:1258)
keycloak_1 | at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
keycloak_1 | at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:400)
keycloak_1 | at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
keycloak_1 | at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
keycloak_1 | at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
keycloak_1 | at java.base/java.util.stream.DistinctOps$1$2.end(DistinctOps.java:168)
keycloak_1 | at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
keycloak_1 | at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:503)
keycloak_1 | at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
keycloak_1 | at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
keycloak_1 | at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
keycloak_1 | at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
keycloak_1 | at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543)
keycloak_1 | at org.keycloak.services.managers.AuthenticationManager.executionActions(AuthenticationManager.java:1259)
keycloak_1 | at org.keycloak.services.managers.AuthenticationManager.actionRequired(AuthenticationManager.java:1147)
keycloak_1 | at org.keycloak.services.managers.AuthenticationManager.nextActionAfterAuthentication(AuthenticationManager.java:994)
keycloak_1 | at org.keycloak.services.resources.LoginActionsService.processRequireAction(LoginActionsService.java:996)
keycloak_1 | at org.keycloak.services.resources.LoginActionsService.requiredActionGET(LoginActionsService.java:978)
keycloak_1 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
keycloak_1 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
keycloak_1 | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
keycloak_1 | at java.base/java.lang.reflect.Method.invoke(Method.java:566)
keycloak_1 | at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
keycloak_1 | at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
keycloak_1 | at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
keycloak_1 | at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
keycloak_1 | at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
keycloak_1 | at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
keycloak_1 | at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
keycloak_1 | at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
keycloak_1 | at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
keycloak_1 | at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
keycloak_1 | at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
keycloak_1 | at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
keycloak_1 | at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
keycloak_1 | at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:90)
keycloak_1 | at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
keycloak_1 | at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
keycloak_1 | at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
keycloak_1 | at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:545)
keycloak_1 | at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
keycloak_1 | at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
keycloak_1 | at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
keycloak_1 | at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
keycloak_1 | at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
keycloak_1 | at java.base/java.lang.Thread.run(Thread.java:829)
keycloak_1 | Caused by: freemarker.template.TemplateNotFoundException: Template not found for name "update-mobile-number.ftl".
keycloak_1 | The name was interpreted by this TemplateLoader: org.keycloak.theme.FreeMarkerUtil$ThemeTemplateLoader@418d109c.
keycloak_1 | at freemarker.template.Configuration.getTemplate(Configuration.java:2883)
keycloak_1 | at freemarker.template.Configuration.getTemplate(Configuration.java:2703)
keycloak_1 | at org.keycloak.theme.FreeMarkerUtil.getTemplate(FreeMarkerUtil.java:85)
keycloak_1 | at org.keycloak.theme.FreeMarkerUtil.processTemplate(FreeMarkerUtil.java:58)
keycloak_1 | ... 73 more
keycloak_1 |
keycloak_1 | 2022-08-17 04:47:49,354 ERROR [org.keycloak.headers.DefaultSecurityHeadersProvider] (executor-thread-14) MediaType not set on path /auth/realms/self/login-actions/required-action, with response status 500
keycloak_1 | 2022-08-17 04:47:49,367 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-14) Uncaught server error: javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
keycloak_1 | at org.keycloak.headers.DefaultSecurityHeadersProvider.addHeaders(DefaultSecurityHeadersProvider.java:75)
keycloak_1 | at org.keycloak.services.filters.KeycloakSecurityHeadersFilter.filter(KeycloakSecurityHeadersFilter.java:40)
keycloak_1 | at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:361)
keycloak_1 | at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:252)
keycloak_1 | at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:101)
keycloak_1 | at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:74)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:594)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
keycloak_1 | at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
keycloak_1 | at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
keycloak_1 | at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
keycloak_1 | at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
keycloak_1 | at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
keycloak_1 | at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
keycloak_1 | at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:201)
keycloak_1 | at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:90)
keycloak_1 | at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
keycloak_1 | at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
keycloak_1 | at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
keycloak_1 | at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:545)
keycloak_1 | at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
keycloak_1 | at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
keycloak_1 | at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
keycloak_1 | at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
keycloak_1 | at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
keycloak_1 | at java.base/java.lang.Thread.run(Thread.java:829)
keycloak_1 |
Thanks @xgp for coming up with this.
I didn’t have time yet to test all my extensions, I only noticed that the sms authenticator doesn’t work, without investigating why. But it seems that you are right, no changes in code and it doesn’t work with 19+
With this behavior and the fact that the new admin ui is more than buggy, I can’t recommend upgrading to KC19 atm. Dissappointing.
I’ve got the same error using any of 17, 18 or 19 KC version. Freemarker just cannot find templates in resources/theme-resources directory.
CustomClasspathThemeResourceProviderFactory does not work for me.
Maybe I do smth wrong?
No, don‘t (never) deal with the sources! Take the compiled binaries or the Docker image!
put custom provider to special folders and then build
Yes, create your providers, compile them to a .jar file, put this into the /opt/keycloak/providers folder (when using Docker, in case of deployment into filesystem, look for the providers folder under the KC root folder), then start/build the Keycloak image (see docs what „build“ is meant here).
Could you tell me another thing: how could I use your 2FA with oauth2 authorization? I mean case when client do request for getting jwt token and then pass it to rest api’s to get access?
With KC23 i am seeing the same issue. Unable to load the template. I have wrapped it in the jar file and then went to the build folder and executed: kc.bat build. Later when I am triggering the login, it still says internal error. And the in the KC logs, I could see exception that org.keycloak.forms.login.LoginFormsProvider.createForm(java.lang.String) noSuchMethodError.
When I went back to see the KC documentation, I could not see the createForm method which was available in earlier version. @dasniko Can you please help with the latest version of KC here please.