keycloak-memoizeit

Merge pull request #36 from vrockai/KEYCLOAK-60 KEYCLOAK-60

9/20/2013 11:39:24 AM

Changes

forms/pom.xml 4(+4 -0)

forms/src/main/resources/META-INF/faces-config.xml 13(+0 -13)

forms/src/main/resources/META-INF/resources/forms/access.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/account.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/login.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/login-totp.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/password.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/register.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/social.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/resources/forms/theme/default/access.xhtml 10(+0 -10)

forms/src/main/resources/META-INF/resources/forms/theme/default/account.xhtml 30(+0 -30)

forms/src/main/resources/META-INF/resources/forms/theme/default/login.xhtml 35(+0 -35)

forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.xhtml 32(+0 -32)

forms/src/main/resources/META-INF/resources/forms/theme/default/register.xhtml 47(+0 -47)

forms/src/main/resources/META-INF/resources/forms/theme/default/social.xhtml 10(+0 -10)

forms/src/main/resources/META-INF/resources/forms/theme/default/totp.xhtml 37(+0 -37)

forms/src/main/resources/META-INF/resources/forms/totp.xhtml 2(+0 -2)

forms/src/main/resources/META-INF/web-fragment.xml 16(+0 -16)

pom.xml 5(+5 -0)

Details

forms/pom.xml 4(+4 -0)

diff --git a/forms/pom.xml b/forms/pom.xml
index 8338766..0ad2957 100755
--- a/forms/pom.xml
+++ b/forms/pom.xml
@@ -56,6 +56,10 @@
       			<groupId>com.google.zxing</groupId>
       			<artifactId>javase</artifactId>
 			</dependency>
+		<dependency>
+			<groupId>org.freemarker</groupId>
+			<artifactId>freemarker</artifactId>
+		</dependency>
 	</dependencies>
 
 	<build>
diff --git a/forms/src/main/java/org/keycloak/forms/ErrorBean.java b/forms/src/main/java/org/keycloak/forms/ErrorBean.java
index d184125..ff140bd 100644
--- a/forms/src/main/java/org/keycloak/forms/ErrorBean.java
+++ b/forms/src/main/java/org/keycloak/forms/ErrorBean.java
@@ -21,33 +21,19 @@
  */
 package org.keycloak.forms;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
-
-import org.keycloak.services.resources.flows.FormFlows;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "error")
-@RequestScoped
 public class ErrorBean {
 
     private String summary;
 
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
-
-        summary = (String) request.getAttribute(FormFlows.ERROR_MESSAGE);
+    public ErrorBean(String summary) {
+        this.summary = summary;
     }
 
     public String getSummary() {
         return summary;
     }
 
-}
+}
\ No newline at end of file
diff --git a/forms/src/main/java/org/keycloak/forms/LoginBean.java b/forms/src/main/java/org/keycloak/forms/LoginBean.java
index a028886..5777e91 100644
--- a/forms/src/main/java/org/keycloak/forms/LoginBean.java
+++ b/forms/src/main/java/org/keycloak/forms/LoginBean.java
@@ -24,25 +24,15 @@ package org.keycloak.forms;
 import java.util.LinkedList;
 import java.util.List;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.MultivaluedMap;
 
 import org.keycloak.forms.model.RequiredCredential;
-import org.keycloak.services.resources.flows.FormFlows;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "login")
-@RequestScoped
 public class LoginBean {
 
-    @ManagedProperty(value = "#{realm}")
     private RealmBean realm;
 
     private String username;
@@ -51,13 +41,10 @@ public class LoginBean {
 
     private List<RequiredCredential> requiredCredentials;
 
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
+    public LoginBean(RealmBean realm, MultivaluedMap<String, String> formData){
+
+        this.realm = realm;
 
-        @SuppressWarnings("unchecked")
-        MultivaluedMap<String, String> formData = (MultivaluedMap<String, String>) request.getAttribute(FormFlows.DATA);
         if (formData != null) {
             username = formData.getFirst("username");
             password = formData.getFirst("password");
@@ -69,6 +56,7 @@ public class LoginBean {
                 requiredCredentials.add(new RequiredCredential(c.getType(), c.isSecret(), c.getFormLabel()));
             }
         }
+
     }
 
     public String getUsername() {
diff --git a/forms/src/main/java/org/keycloak/forms/RealmBean.java b/forms/src/main/java/org/keycloak/forms/RealmBean.java
index 8b1dcef..e799b7a 100644
--- a/forms/src/main/java/org/keycloak/forms/RealmBean.java
+++ b/forms/src/main/java/org/keycloak/forms/RealmBean.java
@@ -21,34 +21,21 @@
  */
 package org.keycloak.forms;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
-
 import org.keycloak.services.models.RealmModel;
-import org.keycloak.services.resources.flows.FormFlows;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "realm")
-@RequestScoped
 public class RealmBean {
 
     private RealmModel realm;
 
     private boolean saas;
 
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
-
-        realm = (RealmModel) request.getAttribute(FormFlows.REALM);
 
-        saas = RealmModel.DEFAULT_REALM.equals(realm.getName());
+    public RealmBean(RealmModel realmModel) {
+        realm = realmModel;
+        saas = RealmModel.DEFAULT_REALM.equals(realmModel.getName());
     }
 
     public String getId() {
@@ -59,7 +46,7 @@ public class RealmBean {
         return saas ? "Keycloak" : realm.getName();
     }
 
-    RealmModel getRealm() {
+    public RealmModel getRealm() {
         return realm;
     }
 
diff --git a/forms/src/main/java/org/keycloak/forms/RegisterBean.java b/forms/src/main/java/org/keycloak/forms/RegisterBean.java
index 77d6a8c..cb8c83d 100755
--- a/forms/src/main/java/org/keycloak/forms/RegisterBean.java
+++ b/forms/src/main/java/org/keycloak/forms/RegisterBean.java
@@ -24,39 +24,23 @@ package org.keycloak.forms;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.MultivaluedMap;
 
-import org.keycloak.services.resources.flows.FormFlows;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "register")
-@RequestScoped
 public class RegisterBean {
 
-    private HashMap<String, String> formData;
+    private Map<String, String> formData = new HashMap<String, String>();
 
     private boolean socialRegistration;
 
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
+    public RegisterBean(MultivaluedMap<String, String> formData, boolean socialRegistration) {
 
         this.formData = new HashMap<String, String>();
 
-        Boolean socialRegistrationAttr = (Boolean)request.getAttribute(FormFlows.SOCIAL_REGISTRATION);
-        this.socialRegistration = socialRegistrationAttr != null && socialRegistrationAttr;
+        this.socialRegistration = socialRegistration;
 
-        @SuppressWarnings("unchecked")
-        MultivaluedMap<String, String> formData = (MultivaluedMap<String, String>) request.getAttribute(FormFlows.DATA);
         if (formData != null) {
             for (String k : formData.keySet()) {
                 this.formData.put(k, formData.getFirst(k));
diff --git a/forms/src/main/java/org/keycloak/forms/SocialBean.java b/forms/src/main/java/org/keycloak/forms/SocialBean.java
index fab91b1..2492ca9 100644
--- a/forms/src/main/java/org/keycloak/forms/SocialBean.java
+++ b/forms/src/main/java/org/keycloak/forms/SocialBean.java
@@ -26,10 +26,6 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
 import javax.imageio.spi.ServiceRegistry;
 import javax.ws.rs.core.UriBuilder;
 
@@ -39,28 +35,24 @@ import org.keycloak.services.resources.flows.Urls;
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "social")
-@RequestScoped
 public class SocialBean {
 
-    @ManagedProperty(value = "#{realm}")
     private RealmBean realm;
 
-    @ManagedProperty(value = "#{register}")
     private RegisterBean registerBean;
 
-    @ManagedProperty(value = "#{url}")
     private UrlBean url;
 
     private List<SocialProvider> providers;
 
-    private UriBuilder socialLoginUrlBuilder;
+    public SocialBean(RealmBean realm, RegisterBean registerBean, UrlBean url) {
+        this.realm = realm;
+        this.registerBean = registerBean;
+        this.url = url;
 
-    @PostConstruct
-    public void init() {
         URI baseURI = url.getBaseURI();
 
-        socialLoginUrlBuilder = UriBuilder.fromUri(Urls.socialRedirectToProviderAuth(baseURI, realm.getId()));
+        UriBuilder socialLoginUrlBuilder = UriBuilder.fromUri(Urls.socialRedirectToProviderAuth(baseURI, realm.getId()));
 
         providers = new LinkedList<SocialProvider>();
         for (Iterator<org.keycloak.social.SocialProvider> itr = ServiceRegistry
diff --git a/forms/src/main/java/org/keycloak/forms/TemplateBean.java b/forms/src/main/java/org/keycloak/forms/TemplateBean.java
index 841d5f0..724aa0e 100644
--- a/forms/src/main/java/org/keycloak/forms/TemplateBean.java
+++ b/forms/src/main/java/org/keycloak/forms/TemplateBean.java
@@ -24,23 +24,14 @@ package org.keycloak.forms;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "template")
-@RequestScoped
 public class TemplateBean {
 
-    @ManagedProperty(value = "#{realm}")
     private RealmBean realm;
 
-    private String theme;
+    private String theme = "default";
 
     private String themeUrl;
 
@@ -48,9 +39,9 @@ public class TemplateBean {
 
     private String formsPath;
 
-    @PostConstruct
-    public void init() {
-        formsPath = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + "/forms";
+
+    public TemplateBean(RealmBean realm, String contextPath) {
+        formsPath = contextPath + "/forms";
 
         // TODO Get theme name from realm
         theme = "default";
@@ -60,6 +51,7 @@ public class TemplateBean {
 
         themeConfig.put("styles", themeUrl + "/styles.css");
 
+        // TODO move this into CSS
         if (realm.isSaas()) {
             themeConfig.put("logo", themeUrl + "/img/red-hat-logo.png");
             themeConfig.put("background", themeUrl + "/img/login-screen-background.jpg");
diff --git a/forms/src/main/java/org/keycloak/forms/TotpBean.java b/forms/src/main/java/org/keycloak/forms/TotpBean.java
index b144574..4ae9cdd 100644
--- a/forms/src/main/java/org/keycloak/forms/TotpBean.java
+++ b/forms/src/main/java/org/keycloak/forms/TotpBean.java
@@ -25,39 +25,28 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.Random;
 
-import javax.annotation.PostConstruct;
-import javax.faces.application.FacesMessage;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-
 import org.picketlink.common.util.Base32;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "totp")
-@RequestScoped
 public class TotpBean {
 
-    @ManagedProperty(value = "#{user}")
     private UserBean user;
 
     private String totpSecret;
     private String totpSecretEncoded;
+    private String contextUrl;
 
-    @PostConstruct
-    public void init() {
-        FacesContext facesContext = FacesContext.getCurrentInstance();
-        FacesMessage facesMessage = new FacesMessage("This is a message");
-        facesContext.addMessage(null, facesMessage);
+    public TotpBean(UserBean user, String contextUrl) {
+        this.user = user;
+        this.contextUrl = contextUrl;
 
         totpSecret = randomString(20);
         totpSecretEncoded = Base32.encode(totpSecret.getBytes());
     }
 
-    private static final String randomString(int length) {
+    private static String randomString(int length) {
         String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW1234567890";
         Random r = new Random();
         StringBuilder sb = new StringBuilder();
@@ -89,8 +78,7 @@ public class TotpBean {
 
     public String getTotpSecretQrCodeUrl() throws UnsupportedEncodingException {
         String contents = URLEncoder.encode("otpauth://totp/keycloak?secret=" + totpSecretEncoded, "utf-8");
-        String contextPath = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath();
-        return contextPath + "/forms/qrcode" + "?size=200x200&contents=" + contents;
+        return contextUrl + "/forms/qrcode" + "?size=200x200&contents=" + contents;
     }
 
     public UserBean getUser() {
diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java
index f846ee1..b6e2ff3 100644
--- a/forms/src/main/java/org/keycloak/forms/UrlBean.java
+++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java
@@ -23,46 +23,20 @@ package org.keycloak.forms;
 
 import java.net.URI;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.ManagedProperty;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.core.UriBuilder;
-
-import org.keycloak.services.resources.flows.FormFlows;
 import org.keycloak.services.resources.flows.Urls;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "url")
-@RequestScoped
 public class UrlBean {
 
     private URI baseURI;
 
-    @ManagedProperty(value = "#{realm}")
     private RealmBean realm;
 
-    @ManagedProperty(value = "#{register}")
-    private RegisterBean registerBean;
-
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
-
-        UriBuilder b = UriBuilder.fromUri(request.getRequestURI()).replaceQuery(request.getQueryString())
-                .replacePath(request.getContextPath()).path("rest");
-
-        if (request.getAttribute(FormFlows.CODE) != null) {
-            b.queryParam("code", request.getAttribute(FormFlows.CODE));
-        }
-
-        baseURI = b.build();
+    public UrlBean(RealmBean realm, URI baseURI){
+        this.realm = realm;
+        this.baseURI = baseURI;
     }
 
     public RealmBean getRealm() {
@@ -73,14 +47,6 @@ public class UrlBean {
         this.realm = realm;
     }
 
-    public RegisterBean getRegisterBean() {
-        return registerBean;
-    }
-
-    public void setRegisterBean(RegisterBean registerBean) {
-        this.registerBean = registerBean;
-    }
-
     public String getAccessUrl() {
         return Urls.accountAccessPage(baseURI, realm.getId()).toString();
     }
@@ -115,10 +81,7 @@ public class UrlBean {
 
     public String getRegistrationAction() {
         if (realm.isSaas()) {
-            // TODO: saas social registration
             return Urls.saasRegisterAction(baseURI).toString();
-        } else if (registerBean.isSocialRegistration()) {
-            return Urls.socialRegisterAction(baseURI, realm.getId()).toString();
         } else {
             return Urls.realmRegisterAction(baseURI, realm.getId()).toString();
         }
@@ -126,6 +89,7 @@ public class UrlBean {
 
     public String getRegistrationUrl() {
         if (realm.isSaas()) {
+            // TODO: saas social registration
             return Urls.saasRegisterPage(baseURI).toString();
         } else {
             return Urls.realmRegisterPage(baseURI, realm.getId()).toString();
diff --git a/forms/src/main/java/org/keycloak/forms/UserBean.java b/forms/src/main/java/org/keycloak/forms/UserBean.java
index 0be17b3..97fe839 100644
--- a/forms/src/main/java/org/keycloak/forms/UserBean.java
+++ b/forms/src/main/java/org/keycloak/forms/UserBean.java
@@ -21,30 +21,17 @@
  */
 package org.keycloak.forms;
 
-import javax.annotation.PostConstruct;
-import javax.faces.bean.ManagedBean;
-import javax.faces.bean.RequestScoped;
-import javax.faces.context.FacesContext;
-import javax.servlet.http.HttpServletRequest;
-
 import org.keycloak.services.models.UserModel;
-import org.keycloak.services.resources.flows.FormFlows;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-@ManagedBean(name = "user")
-@RequestScoped
 public class UserBean {
 
     private UserModel user;
 
-    @PostConstruct
-    public void init() {
-        FacesContext ctx = FacesContext.getCurrentInstance();
-        HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
-
-        user = (UserModel) request.getAttribute(FormFlows.USER);
+    public UserBean(UserModel user){
+        this.user = user;
     }
 
     public String getFirstName() {
diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
new file mode 100644
index 0000000..41482ad
--- /dev/null
+++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
@@ -0,0 +1,250 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.service;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.forms.ErrorBean;
+import org.keycloak.forms.LoginBean;
+import org.keycloak.forms.RealmBean;
+import org.keycloak.forms.RegisterBean;
+import org.keycloak.forms.SocialBean;
+import org.keycloak.forms.TemplateBean;
+import org.keycloak.forms.TotpBean;
+import org.keycloak.forms.UrlBean;
+import org.keycloak.forms.UserBean;
+import org.keycloak.services.FormService;
+import org.keycloak.services.resources.flows.Pages;
+
+/**
+ * @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
+ */
+public class FormServiceImpl implements FormService {
+
+    private static final Logger log = Logger.getLogger(FormServiceImpl.class);
+
+    private static final String ID = "FormServiceId";
+    private static final String BUNDLE = "org.keycloak.forms.messages";
+    private final Map<String, Command> commandMap = new HashMap<String,Command>();
+
+    public FormServiceImpl(){
+        commandMap.put(Pages.LOGIN, new CommandLogin());
+        commandMap.put(Pages.REGISTER, new CommandRegister());
+        commandMap.put(Pages.ACCOUNT, new CommandAccount());
+        commandMap.put(Pages.PASSWORD, new CommandPassword());
+        commandMap.put(Pages.ACCESS, new CommandAccess());
+        commandMap.put(Pages.LOGIN_TOTP, new CommandLoginTotp());
+        commandMap.put(Pages.SECURITY_FAILURE, new CommandSecurityFailure());
+        commandMap.put(Pages.SOCIAL, new CommandSocial());
+        commandMap.put(Pages.TOTP, new CommandTotp());
+        commandMap.put(Pages.VERIFY_EMAIL, new CommandEmail());
+    }
+
+    public String getId(){
+        return ID;
+    }
+
+    public String process(String pageId, FormServiceDataBean dataBean){
+
+        Map<String, Object> attributes = new HashMap<String, Object>();
+
+        RealmBean realm = new RealmBean(dataBean.getRealm());
+        attributes.put("template", new TemplateBean(realm, dataBean.getContextPath()));
+
+        ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
+        attributes.put("rb", rb);
+
+        if (commandMap.containsKey(pageId)){
+            commandMap.get(pageId).exec(attributes, dataBean);
+        }
+
+        return processFmTemplate(pageId, attributes);
+    }
+
+    private String processFmTemplate(String temp, Map<String, Object> input) {
+
+        Writer out = new StringWriter();
+        Configuration cfg = new Configuration();
+
+        try {
+            cfg.setClassForTemplateLoading(FormServiceImpl.class,"/META-INF/resources");
+            Template template = cfg.getTemplate(temp);
+
+            template.process(input, out);
+        } catch (IOException e) {
+            log.error("Failed to load the template " + temp, e);
+        } catch (TemplateException e) {
+            log.error("Failed to process template " + temp, e);
+        }
+
+        return out.toString();
+    }
+
+    private class CommandTotp implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+            attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
+
+            UserBean user = new UserBean(dataBean.getUserModel());
+            attributes.put("user", user);
+
+            TotpBean totp = new TotpBean(user, dataBean.getContextPath());
+            attributes.put("totp", totp);
+
+            attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+        }
+    }
+
+    private class CommandSocial implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
+        }
+    }
+
+    private class CommandEmail implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+        }
+    }
+
+    private class CommandSecurityFailure implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+        }
+    }
+
+    private class CommandPassword implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+            attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
+            attributes.put("user", new UserBean(dataBean.getUserModel()));
+            attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+        }
+    }
+
+    private class CommandLoginTotp implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            if (dataBean.getError() != null){
+                attributes.put("error", new ErrorBean(dataBean.getError()));
+            }
+
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+
+            UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
+
+            attributes.put("url", url);
+            attributes.put("user", new UserBean(dataBean.getUserModel()));
+            attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+
+            RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
+
+            SocialBean social = new SocialBean(realm, register, url);
+            attributes.put("social", social);
+        }
+    }
+
+    private class CommandAccess implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+            attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
+        }
+    }
+
+    private class CommandAccount implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+            attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
+            attributes.put("user", new UserBean(dataBean.getUserModel()));
+            attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+        }
+    }
+
+    private class CommandLogin implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            if (dataBean.getError() != null){
+                attributes.put("error", new ErrorBean(dataBean.getError()));
+            }
+
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+
+            UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
+
+            attributes.put("url", url);
+            attributes.put("user", new UserBean(dataBean.getUserModel()));
+            attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+
+            RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
+
+            SocialBean social = new SocialBean(realm, register, url);
+            attributes.put("social", social);
+        }
+    }
+
+    private class CommandRegister implements Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+            if (dataBean.getError() != null){
+                attributes.put("error", new ErrorBean(dataBean.getError()));
+            }
+
+            RealmBean realm = new RealmBean(dataBean.getRealm());
+
+            attributes.put("realm", realm);
+
+            UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
+
+            attributes.put("url", url);
+            attributes.put("user", new UserBean(dataBean.getUserModel()));
+
+            RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
+            attributes.put("register", register);
+
+            SocialBean social = new SocialBean(realm, register, url);
+            attributes.put("social", social);
+        }
+    }
+
+    private interface Command {
+        public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean);
+    }
+
+}
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/access.ftl b/forms/src/main/resources/META-INF/resources/forms/access.ftl
new file mode 100644
index 0000000..bcea82a
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/access.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/access.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/account.ftl b/forms/src/main/resources/META-INF/resources/forms/account.ftl
new file mode 100755
index 0000000..5c41000
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/account.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/account.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/error.ftl b/forms/src/main/resources/META-INF/resources/forms/error.ftl
new file mode 100644
index 0000000..3dd69c9
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/error.ftl
@@ -0,0 +1 @@
+template not found
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/login.ftl b/forms/src/main/resources/META-INF/resources/forms/login.ftl
new file mode 100644
index 0000000..6033383
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/login.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/login.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/login-totp.ftl b/forms/src/main/resources/META-INF/resources/forms/login-totp.ftl
new file mode 100644
index 0000000..9bd8509
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/login-totp.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/login-totp.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/password.ftl b/forms/src/main/resources/META-INF/resources/forms/password.ftl
new file mode 100644
index 0000000..6fd1a4f
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/password.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/password.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/register.ftl b/forms/src/main/resources/META-INF/resources/forms/register.ftl
new file mode 100644
index 0000000..ff86d85
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/register.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/register.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/social.ftl b/forms/src/main/resources/META-INF/resources/forms/social.ftl
new file mode 100644
index 0000000..baf129e
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/social.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/social.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/access.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/access.ftl
new file mode 100755
index 0000000..a4b2413
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/access.ftl
@@ -0,0 +1,11 @@
+<#import "template-main.ftl" as layout>
+<@layout.mainLayout ; section>
+
+    <#if section = "header">
+
+    Authorized Applications
+
+    <#elseif section = "content">
+
+    </#if>
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl
new file mode 100755
index 0000000..252437b
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl
@@ -0,0 +1,33 @@
+<#import "template-main.ftl" as layout>
+<@layout.mainLayout ; section>
+
+    <#if section = "header">
+
+    Edit Account
+
+    <#elseif section = "content">
+
+    <form action="${url.accountUrl}" method="post">
+        <div>
+            <label for="firstName">${rb.getString('firstName')}</label>
+            <input type="text" id="firstName" name="firstName" value="${user.firstName?default('')}" />
+        </div>
+        <div>
+            <label for="lastName">${rb.getString('lastName')}</label>
+            <input type="text" id="lastName" name="lastName" value="${user.lastName?default('')}" />
+        </div>
+        <div>
+            <label for="email">${rb.getString('email')}</label>
+            <input type="text" id="email" name="email" value="${user.email?default('')}" />
+        </div>
+        <div>
+            <label for="username">${rb.getString('username')}</label>
+            <input type="text" id="username" name="username" value="${user.username?default('')}" disabled="true" />
+        </div>
+
+        <input type="button" value="Cancel" />
+        <input type="submit" value="Save" />
+    </form>
+
+    </#if>
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl
new file mode 100755
index 0000000..80994d6
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login.ftl
@@ -0,0 +1,42 @@
+<#import "template-login.ftl" as layout>
+<@layout.registrationLayout bodyClass=""; section>
+    <#if section = "title">
+
+    Log in to ${realm.name}
+
+    <#elseif section = "header">
+
+    Log in to <strong>${(realm.name)?default('')}</strong>
+
+    <#elseif section = "form">
+
+    <div name="form">
+        <form action="${url.loginAction?default('')}" method="post">
+            <div>
+                <label for="username">${rb.getString('username')}</label>
+                <input id="username" name="username" value="${login.username?default('')}" type="text" />
+            </div>
+
+            <#list login.requiredCredentials as c>
+                <div>
+                    <label for="${c.name}">${rb.getString(c.label)}</label> <input id="${c.name}" name="${c.name}" type="${c.inputType}" />
+                </div>
+            </#list>
+
+            <div class="aside-btn">
+            </div>
+
+            <input type="submit" value="Log In" />
+        </form>
+    </div>
+
+    <#elseif section = "info" >
+
+    <div name="info">
+        <#if realm.registrationAllowed>
+            <p>${rb.getString('noAccount')} <a href="${url.registrationUrl?default('')}">${rb.getString('register')}</a>.</p>
+        </#if>
+    </div>
+
+    </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl
new file mode 100755
index 0000000..5ca1109
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl
@@ -0,0 +1,38 @@
+<#import "template-login.ftl" as layout>
+<@layout.registrationLayout bodyClass=""; section>
+
+    <#if section = "title">
+
+    Log in to ${realm.name}
+
+    <#elseif section = "header">
+
+    Log in to <strong>${realm.name}</strong>
+
+    <#elseif section = "form">
+
+    <form action="${url.loginAction}" method="post">
+        <input id="username" name="username" value="${login.username?default('')}" type="hidden" />
+        <input id="password" name="password" value="${login.password?default('')}" type="hidden" />
+
+        <div>
+            <label for="totp">${rb.getString('authenticatorCode')}</label>
+            <input id="totp" name="totp" type="text" />
+        </div>
+
+        <div class="aside-btn">
+            <!-- <input type="checkbox" id="remember" /><label for="remember">Remember Username</label> -->
+            <!-- <p>Forgot <a href="#">Username</a> or <a href="#">Password</a>?</p> -->
+        </div>
+
+        <input type="submit" value="Log In" />
+    </form>
+
+    <#elseif section = "info">
+
+        <#if realm.registrationAllowed>
+        <p>${rb.getString('noAccount')} <a href="${url.registrationUrl}">${rb.getString('register')}</a>.</p>
+        </#if>
+
+    </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/register.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/register.ftl
new file mode 100755
index 0000000..a8f1eed
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/register.ftl
@@ -0,0 +1,49 @@
+<#import "template-login.ftl" as layout>
+<@layout.registrationLayout bodyClass="register" ; section>
+
+    <#if section = "title">
+
+    ${rb.getString('registerWith')} ${realm.name}
+
+    <#elseif section = "header">
+
+    ${rb.getString('registerWith')} <strong>${realm.name}</strong>
+
+    <#elseif section = "form">
+
+    <form action="${url.registrationAction}" method="post">
+        <p class="subtitle">${rb.getString('allRequired')}</p>
+        <div>
+            <label for="name">${rb.getString('fullName')}</label>
+            <input type="text" id="name" name="name" value="${register.formData.name?default('')}" />
+        </div>
+        <div>
+            <label for="email">${rb.getString('email')}</label>
+            <input type="text" id="email" name="email" value="${register.formData.email?default('')}" />
+        </div>
+        <div>
+            <label for="username">${rb.getString('username')}</label>
+            <input type="text" id="username" name="username" value="${register.formData.username?default('')}" />
+        </div>
+        <div>
+            <label for="password">${rb.getString('password')}</label>
+            <input type="password" id="password" name="password" />
+        </div>
+        <div>
+            <label for="password-confirm">${rb.getString('passwordConfirm')}</label>
+            <input type="password" id="password-confirm" name="password-confirm" />
+        </div>
+
+        <div class="aside-btn">
+            <p>By registering you agree to the <a href="#">Terms of Service</a> and the <a href="#">Privacy Policy</a>.</p>
+        </div>
+
+        <input type="submit" value="Register" />
+    </form>
+
+    <#elseif section = "info">
+
+    <p>${rb.getString('alreadyHaveAccount')} <a href="${url.loginUrl}">${rb.getString('logIn')}</a>.</p>
+
+    </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/social.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/social.ftl
new file mode 100755
index 0000000..97e51c4
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/social.ftl
@@ -0,0 +1,12 @@
+<#import "template-main.ftl" as layout>
+<@layout.mainLayout ; section>
+
+    <#if section = "header">
+
+    Social Accounts
+
+    <#elseif section = "content">
+
+
+    </#if>
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
new file mode 100755
index 0000000..84c8da7
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
@@ -0,0 +1,38 @@
+<#import "template-main.ftl" as layout>
+<@layout.mainLayout ; section>
+
+    <#if section = "header">
+
+    Google Authenticator Setup
+
+    <#elseif section = "content">
+
+    <!--h:messages globalOnly="true" /-->
+        <#if totp.enabled>
+        Google Authenticator enabled
+        <#else>
+        <h2>To setup Google Authenticator</h2>
+
+        <ol>
+            <li>Install Google Authenticator to your device</li>
+            <li>Set up an account in Google Authenticator and scan the QR code below or enter the key<br />
+                <img src="${totp.totpSecretQrCodeUrl}" /> ${totp.totpSecretEncoded}
+            </li>
+            <li>Enter a one-time password provided by Google Authenticator and click Save to finish the setup
+
+                <form action="${url.totpUrl}" method="post">
+                    <div>
+                        <label for="totp">${rb.getString('authenticatorCode')}</label>
+                        <input type="text" id="totp" name="totp" />
+                        <input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
+                    </div>
+
+                    <input type="button" value="Cancel" />
+                    <input type="submit" value="Save" />
+                </form>
+            </li>
+        </ol>
+        </#if>
+
+    </#if>
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/totp.ftl b/forms/src/main/resources/META-INF/resources/forms/totp.ftl
new file mode 100644
index 0000000..ad15b2c
--- /dev/null
+++ b/forms/src/main/resources/META-INF/resources/forms/totp.ftl
@@ -0,0 +1 @@
+<#include "./theme/" + template.theme + "/totp.ftl">
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/services/org.keycloak.services.FormService b/forms/src/main/resources/META-INF/services/org.keycloak.services.FormService
new file mode 100644
index 0000000..482f65d
--- /dev/null
+++ b/forms/src/main/resources/META-INF/services/org.keycloak.services.FormService
@@ -0,0 +1 @@
+org.keycloak.service.FormServiceImpl
\ No newline at end of file

pom.xml 5(+5 -0)

diff --git a/pom.xml b/pom.xml
index cfe307f..c4a3486 100755
--- a/pom.xml
+++ b/pom.xml
@@ -191,6 +191,11 @@
                 <artifactId>google-api-client</artifactId>
                 <version>1.14.1-beta</version>
             </dependency>
+            <dependency>
+                <groupId>org.freemarker</groupId>
+                <artifactId>freemarker</artifactId>
+                <version>2.3.19</version>
+            </dependency>
             
             <!-- Google+ -->
             <dependency>
diff --git a/services/src/main/java/org/keycloak/services/FormService.java b/services/src/main/java/org/keycloak/services/FormService.java
new file mode 100644
index 0000000..8ddf517
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/FormService.java
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.services;
+
+import java.net.URI;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.keycloak.services.models.RealmModel;
+import org.keycloak.services.models.UserModel;
+
+/**
+ * @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
+ */
+public interface FormService {
+
+    String getId();
+
+    public String process(String pageId, FormServiceDataBean data);
+
+    public static class FormServiceDataBean {
+
+        private RealmModel realm;
+        private UserModel userModel;
+        private String error;
+        private MultivaluedMap<String, String> formData;
+        private URI baseURI;
+
+        public Boolean getSocialRegistration() {
+            return socialRegistration;
+        }
+
+        public void setSocialRegistration(Boolean socialRegistration) {
+            this.socialRegistration = socialRegistration;
+        }
+
+        private Boolean socialRegistration;
+
+        public String getCode() {
+            return code;
+        }
+
+        public void setCode(String code) {
+            this.code = code;
+        }
+
+        private String code;
+
+        public String getContextPath() {
+            return contextPath;
+        }
+
+        public void setContextPath(String contextPath) {
+            this.contextPath = contextPath;
+        }
+
+        private String contextPath;
+
+        public FormServiceDataBean(RealmModel realm, UserModel userModel, MultivaluedMap<String, String> formData, String error){
+            this.realm = realm;
+            this.userModel = userModel;
+            this.formData = formData;
+            this.error = error;
+        }
+
+        public URI getBaseURI() {
+            return baseURI;
+        }
+
+        public void setBaseURI(URI baseURI) {
+            this.baseURI = baseURI;
+        }
+
+        public String getError() {
+            return error;
+        }
+
+        public void setError(String error) {
+            this.error = error;
+        }
+
+        public MultivaluedMap<String, String> getFormData() {
+            return formData;
+        }
+
+        public void setFormData(MultivaluedMap<String, String> formData) {
+            this.formData = formData;
+        }
+
+        public RealmModel getRealm() {
+            return realm;
+        }
+
+        public RealmModel setRealm(RealmModel realm) {
+            return realm;
+        }
+
+        public UserModel getUserModel() {
+            return userModel;
+        }
+
+        public void setUserModel(UserModel userModel) {
+            this.userModel = userModel;
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
index 7b08e5c..c33be74 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
@@ -21,15 +21,22 @@
  */
 package org.keycloak.services.resources.flows;
 
+import java.net.URI;
+import java.util.Iterator;
+
 import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+import org.keycloak.services.FormService;
 import org.keycloak.services.email.EmailSender;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.models.UserModel.RequiredAction;
 import org.picketlink.idm.model.sample.Realm;
 
+import javax.imageio.spi.ServiceRegistry;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
 /**
@@ -85,29 +92,43 @@ public class FormFlows {
         return forwardToForm(Pages.ACCOUNT);
     }
 
-    private Response forwardToForm(String form) {
-        request.setAttribute(REALM, realm);
+    private Response forwardToForm(String template) {
 
-        if (error != null) {
-            request.setAttribute(ERROR_MESSAGE, error);
-        }
+        FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error);
 
-        if (formData != null) {
-            request.setAttribute(DATA, formData);
-        }
+        // Getting URI needed by form processing service
+        ResteasyUriInfo uriInfo = request.getUri();
+        MultivaluedMap<String, String> queryParameterMap = uriInfo.getQueryParameters();
+
+        String requestURI = uriInfo.getBaseUri().getPath();
+        UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
 
-        if (userModel != null) {
-            request.setAttribute(USER, userModel);
+        for(String k : queryParameterMap.keySet()){
+            uriBuilder.replaceQueryParam(k, queryParameterMap.get(k).toArray());
         }
 
-        if (code != null) {
-            request.setAttribute(CODE, code);
+        if (code != null){
+            uriBuilder.queryParam(CODE, code);
         }
 
-        request.setAttribute(SOCIAL_REGISTRATION, socialRegistration);
+        URI baseURI = uriBuilder.build();
+        formDataBean.setBaseURI(baseURI);
+
+        // TODO find a better way to obtain contextPath
+        // Getting context path by removing "rest/" substring from the BaseUri path
+        formDataBean.setContextPath(requestURI.substring(0,requestURI.length()-5));
+        formDataBean.setSocialRegistration(socialRegistration);
+
+        // Find the service and process relevant template
+        Iterator<FormService> itr = ServiceRegistry.lookupProviders(FormService.class);
+
+        while (itr.hasNext()) {
+            FormService provider = itr.next();
+            if (provider.getId().equals("FormServiceId"))
+                return Response.status(200).entity(provider.process(template, formDataBean)).build();
+        }
 
-        request.forward(form);
-        return null;
+        return Response.status(200).entity("form provider not found").build();
     }
 
     public Response forwardToLogin() {
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
index a1e770e..123b1dd 100644
--- a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
@@ -26,26 +26,26 @@ package org.keycloak.services.resources.flows;
  */
 public class Pages {
 
-    public final static String ACCESS = "/forms/access.xhtml";
+    public final static String ACCESS = "/forms/access.ftl";
 
-    public final static String ACCOUNT = "/forms/account.xhtml";
+    public final static String ACCOUNT = "/forms/account.ftl";
 
-    public final static String LOGIN = "/forms/login.xhtml";
+    public final static String LOGIN = "/forms/login.ftl";
 
-    public final static String LOGIN_TOTP = "/forms/login-totp.xhtml";
+    public final static String LOGIN_TOTP = "/forms/login-totp.ftl";
 
     public final static String OAUTH_GRANT = "/saas/oauthGrantForm.jsp";
 
-    public final static String PASSWORD = "/forms/password.xhtml";
+    public final static String PASSWORD = "/forms/password.ftl";
 
-    public final static String REGISTER = "/forms/register.xhtml";
+    public final static String REGISTER = "/forms/register.ftl";
 
     public final static String SECURITY_FAILURE = "/saas/securityFailure.jsp";
 
-    public final static String SOCIAL = "/forms/social.xhtml";
+    public final static String SOCIAL = "/forms/social.ftl";
 
-    public final static String TOTP = "/forms/totp.xhtml";
+    public final static String TOTP = "/forms/totp.ftl";
 
-    public final static String VERIFY_EMAIL = "/forms/verify-email.xhtml";
+    public final static String VERIFY_EMAIL = "/forms/verify-email.ftl";
 
 }