You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2017/03/31 13:27:06 UTC

[10/15] syncope git commit: [SYNCOPE-1041] SAML 2.0 SP extension: enduser components

[SYNCOPE-1041] SAML 2.0 SP extension: enduser components


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/14774113
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/14774113
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/14774113

Branch: refs/heads/2_0_X
Commit: 14774113e16466a0234fd4e6c78fdd5a7fa254d3
Parents: 9bbd150
Author: Francesco Chicchiricc� <il...@apache.org>
Authored: Fri Mar 24 14:12:38 2017 +0100
Committer: Francesco Chicchiricc� <il...@apache.org>
Committed: Fri Mar 31 15:25:11 2017 +0200

----------------------------------------------------------------------
 .../client/enduser/SyncopeEnduserSession.java   | 32 +++++++--
 .../client/enduser/commons/Constants.java       | 28 ++++++++
 .../syncope/client/enduser/pages/HomePage.java  | 16 ++++-
 .../syncope/client/enduser/pages/Logout.java    | 42 ++++++++++++
 .../enduser/resources/AnyTypeClassResource.java |  5 +-
 .../enduser/resources/AnyTypeResource.java      |  3 +-
 .../resources/ExternalResourceResource.java     |  6 +-
 .../client/enduser/resources/GroupResource.java |  3 +-
 .../client/enduser/resources/InfoResource.java  |  2 +
 .../client/enduser/resources/LoginResource.java |  5 +-
 .../enduser/resources/LogoutResource.java       | 40 -----------
 .../client/enduser/resources/RealmResource.java |  3 +-
 .../enduser/resources/SchemaResource.java       |  5 +-
 .../SecurityQuestionByUsernameResource.java     |  4 +-
 .../resources/SecurityQuestionsResource.java    |  3 +-
 .../resources/UserSelfChangePassword.java       |  5 +-
 .../resources/UserSelfConfirmPasswordReset.java |  6 +-
 .../resources/UserSelfCreateResource.java       |  8 +--
 .../enduser/resources/UserSelfIsLogged.java     |  6 +-
 .../resources/UserSelfPasswordReset.java        |  5 +-
 .../enduser/resources/UserSelfReadResource.java |  4 +-
 .../resources/UserSelfUpdateResource.java       |  5 +-
 .../META-INF/resources/app/css/login.css        | 25 +------
 .../resources/META-INF/resources/app/index.html |  1 +
 .../resources/META-INF/resources/app/js/app.js  | 64 ++++++++++-------
 .../app/js/controllers/LoginController.js       | 24 ++++---
 .../app/js/controllers/UserController.js        | 47 +++++--------
 .../resources/app/js/services/authService.js    | 14 ----
 .../app/js/services/saml2IdPService.js          | 41 +++++++++++
 .../META-INF/resources/app/views/editUser.html  |  2 +-
 .../resources/app/views/passwordreset.html      |  4 +-
 .../META-INF/resources/app/views/self.html      | 13 +++-
 .../resources/app/views/user-credentials.html   |  2 +-
 .../app/views/user-derived-schemas.html         |  2 +-
 .../resources/app/views/user-form-finish.html   |  2 +-
 .../resources/app/views/user-groups.html        |  2 +-
 .../resources/app/views/user-plain-schemas.html |  2 +-
 .../resources/app/views/user-resources.html     |  2 +-
 .../app/views/user-virtual-schemas.html         |  2 +-
 .../ext/saml2lsp/agent/AssertionConsumer.java   |  5 +-
 .../syncope/ext/saml2lsp/agent/Login.java       |  5 +-
 .../syncope/ext/saml2lsp/agent/Logout.java      |  8 ++-
 .../client/console/pages/SAML2SPLogin.java      |  6 +-
 .../enduser/pages/SAML2SPBeforeLogout.java      | 37 ++++++++++
 .../client/enduser/pages/SAML2SPLogin.java      | 67 ++++++++++++++++++
 .../client/enduser/pages/SAML2SPLogout.java     | 36 ++++++++++
 .../enduser/resources/SAML2IdPsResource.java    | 72 ++++++++++++++++++++
 .../syncope/core/logic/SAML2IdPLogic.java       | 25 +++++--
 fit/enduser-reference/pom.xml                   |  6 ++
 .../src/main/resources/saml2sp-agent.properties | 26 +++++++
 .../src/main/webapp/WEB-INF/web.xml             | 20 +++++-
 .../src/test/resources/rebel.xml                |  8 +++
 52 files changed, 599 insertions(+), 207 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
index df2f73f..168656c 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
@@ -94,6 +94,15 @@ public class SyncopeEnduserSession extends WebSession {
         });
     }
 
+    private void afterAuthentication() {
+        Pair<Map<String, Set<String>>, UserTO> self = client.self();
+        selfTO = self.getValue();
+
+        // bind explicitly this session to have a stateful behavior during http requests, unless session will
+        // expire for every request
+        this.bind();
+    }
+
     public boolean authenticate(final String username, final String password) {
         boolean authenticated = false;
 
@@ -102,12 +111,25 @@ public class SyncopeEnduserSession extends WebSession {
                     setDomain(SyncopeEnduserApplication.get().getDomain()).
                     create(username, password);
 
-            Pair<Map<String, Set<String>>, UserTO> self = client.self();
-            selfTO = self.getValue();
+            afterAuthentication();
+
+            authenticated = true;
+        } catch (Exception e) {
+            LOG.error("Authentication failed", e);
+        }
+
+        return authenticated;
+    }
+
+    public boolean authenticate(final String jwt) {
+        boolean authenticated = false;
+
+        try {
+            client = SyncopeEnduserApplication.get().getClientFactory().
+                    setDomain(SyncopeEnduserApplication.get().getDomain()).create(jwt);
+
+            afterAuthentication();
 
-            // bind explicitly this session to have a stateful behavior during http requests, unless session will
-            // expire for every request
-            this.bind();
             authenticated = true;
         } catch (Exception e) {
             LOG.error("Authentication failed", e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/commons/Constants.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/commons/Constants.java
new file mode 100644
index 0000000..4a8189c
--- /dev/null
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/commons/Constants.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.commons;
+
+public final class Constants {
+
+    public static final String BEFORE_LOGOUT = "beforeLogoutPage";
+
+    private Constants() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/HomePage.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/HomePage.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/HomePage.java
index 4436994..4c017ca 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/HomePage.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/HomePage.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.client.enduser.pages;
 
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import org.apache.wicket.NonResettingRestartException;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
@@ -28,7 +30,17 @@ public class HomePage extends WebPage {
 
     public HomePage(final PageParameters parameters) {
         super(parameters);
-        throw new NonResettingRestartException("/app/");
-    }
 
+        StringBuilder redirectUrl = new StringBuilder("/app/");
+        if (!parameters.get("errorMessage").isNull()) {
+            redirectUrl.append("#!self?errorMessage=");
+            try {
+                redirectUrl.append(
+                        URLEncoder.encode(parameters.get("errorMessage").toString(), StandardCharsets.UTF_8.name()));
+            } catch (Exception e) {
+                redirectUrl.append("Generic error");
+            }
+        }
+        throw new NonResettingRestartException(redirectUrl.toString());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Logout.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Logout.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Logout.java
new file mode 100644
index 0000000..37e3103
--- /dev/null
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Logout.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.Constants;
+import org.apache.wicket.markup.html.WebPage;
+
+public class Logout extends WebPage {
+
+    private static final long serialVersionUID = -4514869014471715933L;
+
+    public Logout() {
+        super();
+
+        @SuppressWarnings("unchecked")
+        Class<? extends WebPage> beforeLogout = (Class<? extends WebPage>) SyncopeEnduserSession.get().
+                getAttribute(Constants.BEFORE_LOGOUT);
+        if (beforeLogout == null) {
+            SyncopeEnduserSession.get().invalidateNow();
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            setResponsePage(beforeLogout);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeClassResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeClassResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeClassResource.java
index 1fda74f..f9b906e9 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeClassResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeClassResource.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -36,13 +37,11 @@ public class AnyTypeClassResource extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final Attributes attributes) {
-
         LOG.debug("Get all available auxiliary classes");
 
         ResourceResponse response = new ResourceResponse();
-
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
-
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
                 LOG.error("XSRF TOKEN does not match");

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeResource.java
index 87ee08c..3af7093 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/AnyTypeResource.java
@@ -21,6 +21,7 @@ package org.apache.syncope.client.enduser.resources;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -38,7 +39,7 @@ public class AnyTypeResource extends BaseResource {
         LOG.debug("Get all available auxiliary classes");
 
         ResourceResponse response = new ResourceResponse();
-
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
 
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/ExternalResourceResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/ExternalResourceResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/ExternalResourceResource.java
index 4c69d67..30c518e 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/ExternalResourceResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/ExternalResourceResource.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -39,10 +40,9 @@ public class ExternalResourceResource extends BaseResource {
     protected AbstractResource.ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
         LOG.debug("Search all available resources");
 
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
-
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
                 LOG.error("XSRF TOKEN does not match");

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/GroupResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/GroupResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/GroupResource.java
index 93cd002..c885e71 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/GroupResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/GroupResource.java
@@ -23,6 +23,7 @@ import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -43,8 +44,8 @@ public class GroupResource extends BaseResource {
         LOG.debug("Search all available groups");
 
         ResourceResponse response = new ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
-
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
                 LOG.error("XSRF TOKEN does not match");

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
index 172628e..bd4d30f 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.enduser.resources;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserConstants;
@@ -38,6 +39,7 @@ public class InfoResource extends BaseResource {
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
         ResourceResponse response = new ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
 
         try {
             final CookieUtils sessionCookieUtils = SyncopeEnduserSession.get().getCookieUtils();

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LoginResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LoginResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LoginResource.java
index 7007b9f..b3bf91d 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LoginResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LoginResource.java
@@ -21,6 +21,7 @@ package org.apache.syncope.client.enduser.resources;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserApplication;
@@ -38,8 +39,8 @@ public class LoginResource extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.TEXT_PLAIN);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LogoutResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LogoutResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LogoutResource.java
deleted file mode 100644
index 789729e..0000000
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/LogoutResource.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.resources;
-
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.annotations.Resource;
-
-@Resource(key = "logout", path = "/api/logout")
-public class LogoutResource extends BaseResource {
-
-    private static final long serialVersionUID = -648841355644985051L;
-
-    @Override
-    protected ResourceResponse newResourceResponse(final Attributes attributes) {
-        LOG.debug("Logout from enduser application");
-
-        SyncopeEnduserSession.get().invalidateNow();
-
-        ResourceResponse response = new ResourceResponse();
-        response.setStatusCode(204);
-        return response;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/RealmResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/RealmResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/RealmResource.java
index 6faafaf..8c6d74a 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/RealmResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/RealmResource.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -39,7 +40,7 @@ public class RealmResource extends BaseResource {
         LOG.debug("Search all available realms");
 
         ResourceResponse response = new ResourceResponse();
-
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
index b6a601a..a68704d 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
@@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
@@ -52,8 +53,8 @@ public class SchemaResource extends BaseResource {
     protected AbstractResource.ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
         LOG.debug("Search all {} any type kind related schemas", AnyTypeKind.USER.name());
 
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionByUsernameResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionByUsernameResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionByUsernameResource.java
index 68051ff..419d51b 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionByUsernameResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionByUsernameResource.java
@@ -41,8 +41,8 @@ public class SecurityQuestionByUsernameResource extends BaseResource {
     protected AbstractResource.ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
         LOG.debug("List available security questions");
 
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionsResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionsResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionsResource.java
index 5adba6c..201da2d 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionsResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SecurityQuestionsResource.java
@@ -41,7 +41,7 @@ public class SecurityQuestionsResource extends BaseResource {
         LOG.debug("List available security questions");
 
         AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
 
@@ -61,7 +61,6 @@ public class SecurityQuestionsResource extends BaseResource {
                 }
             });
 
-            response.setContentType(MediaType.APPLICATION_JSON);
             response.setTextEncoding(StandardCharsets.UTF_8.name());
             response.setStatusCode(Response.Status.OK.getStatusCode());
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfChangePassword.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfChangePassword.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfChangePassword.java
index 85e66a9..9d8ed9a 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfChangePassword.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfChangePassword.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -36,8 +37,8 @@ public class UserSelfChangePassword extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfConfirmPasswordReset.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfConfirmPasswordReset.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfConfirmPasswordReset.java
index e9691b3..343ed64 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfConfirmPasswordReset.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfConfirmPasswordReset.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -36,8 +37,8 @@ public class UserSelfConfirmPasswordReset extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
@@ -72,7 +73,6 @@ public class UserSelfConfirmPasswordReset extends BaseResource {
             });
 
             response.setStatusCode(Response.Status.OK.getStatusCode());
-
         } catch (final Exception e) {
             LOG.error("Error while updating user", e);
             response.setError(Response.Status.BAD_REQUEST.getStatusCode(), new StringBuilder()

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
index 010b9b6..2634640 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
@@ -63,9 +64,8 @@ public class UserSelfCreateResource extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final Attributes attributes) {
-        final StringBuilder responseMessage = new StringBuilder();
         ResourceResponse response = new ResourceResponse();
-
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
 
@@ -197,8 +197,8 @@ public class UserSelfCreateResource extends BaseResource {
                     public void writeData(final Attributes attributes) throws IOException {
                         attributes.getResponse().write(res.getStatusInfo().getFamily().equals(
                                 Response.Status.Family.SUCCESSFUL)
-                                        ? responseMessage.append("User: ").append(userTO.getUsername()).append(
-                                                " successfully created")
+                                        ? new StringBuilder().append("User: ").append(userTO.getUsername()).
+                                                append(" successfully created")
                                         : new StringBuilder().append("ErrorMessage{{ ").
                                                 append(res.getStatusInfo().getReasonPhrase()).append(" }}"));
                     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfIsLogged.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfIsLogged.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfIsLogged.java
index bc9f3c5..e153b81 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfIsLogged.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfIsLogged.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.enduser.resources;
 
 import java.io.IOException;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.annotations.Resource;
@@ -32,10 +33,10 @@ public class UserSelfIsLogged extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final Attributes attributes) {
-        ResourceResponse response = new ResourceResponse();
-
         LOG.debug("Checking if user is authenticated");
 
+        ResourceResponse response = new ResourceResponse();
+        response.setContentType(MediaType.TEXT_PLAIN);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
@@ -53,7 +54,6 @@ public class UserSelfIsLogged extends BaseResource {
                 }
             });
             response.setStatusCode(Response.Status.OK.getStatusCode());
-
         } catch (Exception e) {
             LOG.error("Could not read credentials from request", e);
             response.setError(Response.Status.BAD_REQUEST.getStatusCode(), new StringBuilder()

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfPasswordReset.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfPasswordReset.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfPasswordReset.java
index d431cfe..f97d348 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfPasswordReset.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfPasswordReset.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserConstants;
@@ -38,8 +39,8 @@ public class UserSelfPasswordReset extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
index fc6f4fd..8aefb55 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
@@ -46,9 +46,9 @@ public class UserSelfReadResource extends BaseResource {
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
         LOG.debug("Requested user self information");
 
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
         try {
-
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {
                 LOG.error("XSRF TOKEN does not match");

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfUpdateResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfUpdateResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfUpdateResource.java
index dc46553..4a7e78c 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfUpdateResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfUpdateResource.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
@@ -49,8 +50,8 @@ public class UserSelfUpdateResource extends BaseResource {
 
     @Override
     protected ResourceResponse newResourceResponse(final IResource.Attributes attributes) {
-        AbstractResource.ResourceResponse response = new AbstractResource.ResourceResponse();
-
+        ResourceResponse response = new AbstractResource.ResourceResponse();
+        response.setContentType(MediaType.TEXT_PLAIN);
         try {
             HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
             if (!xsrfCheck(request)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/css/login.css
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/css/login.css b/client/enduser/src/main/resources/META-INF/resources/app/css/login.css
index 8999264..a517ae5 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/css/login.css
+++ b/client/enduser/src/main/resources/META-INF/resources/app/css/login.css
@@ -18,13 +18,7 @@
  */
 
 body, html {
-  /*margin: 45px 0;*/
-  /*height: 100%;*/
   background-repeat: no-repeat;
-  /*background-image: linear-gradient(rgb(104, 145, 162), #00a65a);*/
-  /*background-color: #00A65A;
-  height: 100%;
-  background-image: linear-gradient(#6891A2, #00A65A);*/
 }
 
 body{
@@ -39,8 +33,6 @@ body{
   }
 }
 
-
-
 .login-btn {
   background: -moz-linear-gradient(top, #a9db80 0%, #96c56f 100%); /* FF3.6+ */
   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#a9db80), color-stop(100%,#96c56f)); /* Chrome,Safari4+ */
@@ -54,21 +46,6 @@ body{
   background: #658D5D;
 }
 
-/*
-#login {
-  position: relative;
-  padding: 25px 25px 50px 25px;
-  margin-bottom: 1em;
-}
-#login #login-form {
-  margin-top: 2em;
-  margin-bottom: 2em;
-  text-align: left;
-}
-#login-form{
-  padding: 0 195px;
-  margin: 7%;
-}*/
 #languageContainer {
   padding: 0 25px;
 }
@@ -134,4 +111,4 @@ body{
 
 input[type="number"]{
   padding-right: 0px !important
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/index.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/index.html b/client/enduser/src/main/resources/META-INF/resources/app/index.html
index 3c98e23..21a7984 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/index.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/index.html
@@ -98,6 +98,7 @@ under the License.
   <script src="js/services/resourceService.js"></script>
   <script src="js/services/groupService.js"></script>
   <script src="js/services/anyService.js"></script>
+  <script src="js/services/saml2IdPService.js"></script>
   <!--controllers-->
   <script src="js/controllers/HomeController.js"></script>
   <script src="js/controllers/LoginController.js"></script>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
index e8f8fd7..d8ceeb4 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
@@ -48,11 +48,11 @@ var app = angular.module('SyncopeEnduserApp', [
 app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$translateProvider', '$translatePartialLoaderProvider',
   function ($stateProvider, $urlRouterProvider, $httpProvider, $translateProvider, $translatePartialLoaderProvider) {
     /*
-       |--------------------------------------------------------------------------
-       | Syncope Enduser AngularJS providers configuration
-       |--------------------------------------------------------------------------
+     |--------------------------------------------------------------------------
+     | Syncope Enduser AngularJS providers configuration
+     |--------------------------------------------------------------------------
      */
-    
+
     /*
      * i18n provider
      */
@@ -70,7 +70,7 @@ app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$translate
               templateUrl: 'views/self.html'
             })
             .state('self', {
-              url: '/self',
+              url: '/self?errorMessage',
               templateUrl: 'views/self.html'
             })
             .state('user-self-update', {
@@ -250,7 +250,7 @@ app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$translate
         },
         'responseError': function (response) {
           $rootScope.spinner.off();
-          if (response.config.url.indexOf("acceptError=true") === -1) {
+          if (response.config && response.config.url.indexOf("acceptError=true") === -1) {
             var status = response.status;
             if (status === 401) {
               console.error("ERROR ", status);
@@ -270,12 +270,12 @@ app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$translate
 app.run(['$rootScope', '$location', '$state', 'AuthService',
   function ($rootScope, $location, $state, AuthService) {
     /*
-       |--------------------------------------------------------------------------
-       | Main of Syncope Enduser application
-       |
-       | keep user logged in after page refresh
-       | If the route change failed due to authentication error, redirect them out
-       |--------------------------------------------------------------------------
+     |--------------------------------------------------------------------------
+     | Main of Syncope Enduser application
+     |
+     | keep user logged in after page refresh
+     | If the route change failed due to authentication error, redirect them out
+     |--------------------------------------------------------------------------
      */
     $rootScope.$on('$routeChangeError', function (event, current, previous, rejection) {
       if (rejection === 'Not Authenticated') {
@@ -331,12 +331,12 @@ app.run(['$rootScope', '$location', '$state', 'AuthService',
       }
     };
   }]);
-app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService', function ($scope, $rootScope,
-          InfoService) {
+app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'InfoService', 'SAML2IdPService',
+  function ($scope, $rootScope, $location, InfoService, SAML2IdPService) {
     $scope.initApplication = function () {
       /* 
        * disable by default wizard buttons in self-registration
-       */ 
+       */
       $rootScope.endReached = false;
       /*
        |--------------------------------------------------------------------------
@@ -345,9 +345,9 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
        */
       $rootScope.languages = {
         availableLanguages: [
-          { id: '1', name: 'Italiano', code: 'it', format: 'dd/MM/yyyy HH:mm' },
-          { id: '2', name: 'English', code: 'en', format: 'MM/dd/yyyy HH:mm' },
-          { id: '3', name: 'Deutsch', code: 'de', format: 'dd/MM/yyyy HH:mm' }
+          {id: '1', name: 'Italiano', code: 'it', format: 'dd/MM/yyyy HH:mm'},
+          {id: '2', name: 'English', code: 'en', format: 'MM/dd/yyyy HH:mm'},
+          {id: '3', name: 'Deutsch', code: 'de', format: 'dd/MM/yyyy HH:mm'}
         ]
       };
       $rootScope.languages.selectedLanguage = $rootScope.languages.availableLanguages[1];
@@ -363,6 +363,11 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
       $rootScope.pwdResetRequiringSecurityQuestions = false;
       $rootScope.captchaEnabled = false;
       $rootScope.validationEnabled = true;
+      $rootScope.saml2idps = {
+        available: [],
+        selected: {}
+      };
+
       InfoService.getInfo().then(
               function (response) {
                 $rootScope.pwdResetAllowed = response.pwdResetAllowed;
@@ -374,15 +379,28 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
               function (response) {
                 console.error("Something went wrong while accessing info resource", response);
               });
+      SAML2IdPService.getAvailableSAML2IdPs().then(
+              function (response) {
+                $rootScope.saml2idps.available = response;
+              },
+              function (response) {
+                console.debug("No SAML 2.0 SP extension available", response);
+              });
       /* 
        * configuration getters
-       */ 
+       */
       $rootScope.isSelfRegAllowed = function () {
         return $rootScope.selfRegAllowed === true;
       };
       $rootScope.isPwdResetAllowed = function () {
         return $rootScope.pwdResetAllowed === true;
       };
+      $rootScope.saml2spExtAvailable = function () {
+        return $rootScope.saml2idps.available.length > 0;
+      }
+      $rootScope.saml2login = function () {
+        window.location.href = '../saml2sp/login?idp=' + $rootScope.saml2idps.selected.entityID;
+      }
       $rootScope.getVersion = function () {
         return $rootScope.version;
       };
@@ -390,12 +408,12 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
        * USER Attributes sorting strategies
        */
       $rootScope.attributesSorting = {
-        ASC: function ( a, b ) {
+        ASC: function (a, b) {
           var schemaNameA = a.key;
           var schemaNameB = b.key;
           return schemaNameA < schemaNameB ? -1 : schemaNameA > schemaNameB ? 1 : 0;
         },
-        DESC: function ( a, b ) {
+        DESC: function (a, b) {
           var schemaNameA = a.key;
           var schemaNameB = b.key;
           return schemaNameA < schemaNameB ? 1 : schemaNameA > schemaNameB ? -1 : 0;
@@ -405,7 +423,7 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
        |--------------------------------------------------------------------------
        | Notification mgmt
        |--------------------------------------------------------------------------
-       */    
+       */
       $scope.notification = $('#notifications').kendoNotification().data("kendoNotification");
       $scope.notification.setOptions({stacking: "down"});
       $scope.notification.options.position["top"] = 20;
@@ -490,7 +508,7 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
        |--------------------------------------------------------------------------
        | Wizard configuration
        |--------------------------------------------------------------------------
-       */ 
+       */
       $scope.wizard = {
         "credentials": {url: "/credentials"},
         "groups": {url: "/groups"},

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
index af0bfdb..460d12c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
@@ -18,8 +18,8 @@
  */
 
 'use strict';
-angular.module("login").controller("LoginController", ['$scope', '$http', '$location', 'AuthService',
-  function ($scope, $http, $location, AuthService) {
+angular.module("login").controller("LoginController", ['$scope', '$rootScope', '$http', '$location', 'AuthService',
+  function ($scope, $rootScope, $http, $location, AuthService) {
 
     $scope.credentials = {
       username: '',
@@ -32,6 +32,8 @@ angular.module("login").controller("LoginController", ['$scope', '$http', '$loca
         console.info("Login success for: ", user);
         // reset error message
         $scope.credentials.errorMessage = '';
+        // reset SAML 2.0 entityID
+        $rootScope.saml2idps.selected.entityID = null;
         // got to update page
         $location.path("/self/update");
       }, function (response) {
@@ -46,14 +48,11 @@ angular.module("login").controller("LoginController", ['$scope', '$http', '$loca
         $scope.showError($scope.credentials.errorMessage, $scope.notification);
       });
     };
-    
+
     $scope.logout = function () {
-      AuthService.logout().then(function (response) {
-        console.info("Logout successfully");
-      }, function (response) {
-        console.info("Logout failed: ", response);
-      });
+      window.location.href = '../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.Logout';
     };
+
     $scope.islogged = function () {
       AuthService.islogged().then(function (response) {
         console.debug("user login status detected", response);
@@ -83,4 +82,13 @@ angular.module("login").controller("LoginController", ['$scope', '$http', '$loca
         console.debug("schemaAPI response: ", data);
       });
     };
+    $scope.$watch(function () {
+      return $location.search().errorMessage;
+    }, function (errorMessage) {
+      if (errorMessage) {
+        var message = (' ' + errorMessage).slice(1);
+        $scope.showError(message, $scope.notification);
+        delete $location.$$search.errorMessage;
+      }
+    });
   }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
index 4f0cf7a..18ae153 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
@@ -75,15 +75,15 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
           initSchemaValues(schemas);
         }, function (response) {
           // parse error response and log
-          if ( response !== undefined ) {
-            var errorMessages = response.toString().split( "ErrorMessage{{" );
-            if ( errorMessages.length > 1 ) {
-              console.error( "Error retrieving user schemas: ", response.toString().split( "ErrorMessage{{" )[1].split( "}}" )[0] );
+          if (response !== undefined) {
+            var errorMessages = response.toString().split("ErrorMessage{{");
+            if (errorMessages.length > 1) {
+              console.error("Error retrieving user schemas: ", response.toString().split("ErrorMessage{{")[1].split("}}")[0]);
             } else {
-              console.error( "Error retrieving user schemas: ", errorMessages );
+              console.error("Error retrieving user schemas: ", errorMessages);
             }
           }
-        } );
+        });
       };
 
       var initSchemaValues = function (schemas) {
@@ -373,7 +373,7 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
       var wrappedUser = UserUtil.getWrappedUser(user);
       if ($scope.createMode) {
         UserSelfService.create(wrappedUser, $scope.captchaInput.value).then(function (response) {
-          console.info("User " + $scope.user.username + " SUCCESSFULLY_CREATED");
+          console.debug("User " + $scope.user.username + " SUCCESSFULLY_CREATED");
           $rootScope.currentUser = $scope.user.username;
           $rootScope.currentOp = "SUCCESSFULLY_CREATED";
           $state.go('success');
@@ -389,15 +389,12 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
         });
       } else {
         UserSelfService.update(wrappedUser, $scope.captchaInput.value).then(function (response) {
-          console.debug("Updated user: ", response);
-          AuthService.logout().then(function (response) {
-            console.info("User " + $scope.user.username + " SUCCESSFULLY_UPDATED");
-            $rootScope.currentUser = $scope.user.username;
-            $rootScope.currentOp = "SUCCESSFULLY_UPDATED";
-            $state.go('success');
-          }, function () {
-            console.error("LOGOUT FAILED");
-          });
+          console.debug("User " + $scope.user.username + " SUCCESSFULLY_UPDATED");
+          $rootScope.currentUser = $scope.user.username;
+          $rootScope.currentOp = "SUCCESSFULLY_UPDATED";
+          $state.go('success');
+
+          $scope.logout();
         }, function (response) {
           console.info("Error during user update: ", response);
           var errorMessage;
@@ -459,7 +456,6 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
     };
 
     $scope.confirmPasswordReset = function (user, event) {
-
       //getting the enclosing form in order to access to its controller                
       var currentForm = GenericUtil.getEnclosingFormController(event.target, $scope);
       if (currentForm != null) {
@@ -488,7 +484,6 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
     };
 
     $scope.changePassword = function (user, event) {
-
       //getting the enclosing form in order to access to its controller                
       var currentForm = GenericUtil.getEnclosingFormController(event.target, $scope);
       if (currentForm != null) {
@@ -518,18 +513,10 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
       kendo.culture($rootScope.languages.selectedLanguage.code);
     };
 
-    $scope.logout = function (message) {
-      AuthService.logout().then(function (response) {
-        console.info("Logout successfully");
-        $translate.use($scope.languages.selectedLanguage.code);
-        $rootScope.endReached = false;
-        $location.path('/self');
-        if (message) {
-          $scope.showSuccess(message, $scope.notification);
-        }
-      }, function (response) {
-        console.error("Logout failed: ", response);
-      });
+    $scope.logout = function () {
+      $translate.use($scope.languages.selectedLanguage.code);
+      $rootScope.endReached = false;
+      window.location.href = '../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.Logout';
     };
 
     $scope.redirect = function () {

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/js/services/authService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/authService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/authService.js
index 45bef0f..593781a 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/services/authService.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/authService.js
@@ -35,22 +35,10 @@ angular.module('login')
                       });
             };
 
-            authService.logout = function () {
-              return $http
-                      .get('/syncope-enduser/api/logout')
-                      .then(function (response) {
-                        return response;
-                      }, function (response) {
-                        console.error("Something went wrong during logout, exit with status: ", response);
-                      });
-            };
-
-
             authService.islogged = function () {
               return $http
                       .get('/syncope-enduser/api/self/islogged')
                       .then(function (response) {
-//                        console.debug("user logged:", response.data);
                         return response.data;
                       }, function (response) {
                         console.error("error retrieving user login status");
@@ -59,5 +47,3 @@ angular.module('login')
 
             return authService;
           }]);
-
-

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/js/services/saml2IdPService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/saml2IdPService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/saml2IdPService.js
new file mode 100644
index 0000000..e8faddd
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/saml2IdPService.js
@@ -0,0 +1,41 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('self')
+        .factory('SAML2IdPService', ['$resource', '$q', '$http',
+          function ($resource, $q, $http) {
+
+            var saml2IdPService = {};
+
+            saml2IdPService.getAvailableSAML2IdPs = function () {
+              return $http.get("/syncope-enduser/api/saml2IdPs")
+                      .then(function (response) {
+                        return response.data;
+                      }, function (response) {
+                        console.error("Something went wrong during realms retrieval, exit with status: ", response);
+                        return $q.reject(response.data || response.statusText);
+                      });
+            };
+
+            return saml2IdPService;
+          }]);
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
index aee0509..b4ea868 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
@@ -31,7 +31,7 @@ under the License.
           <div class="row">
             <div growl reference="2" inline="true"></div>              
             <div id="status-buttons" class="btn-group btn-breadcrumb">
-              <a href="#/self" class="btn btn-default" ng-show="createMode" ng-click="logout()"><i class="glyphicon glyphicon-home"></i></a>
+              <a class="btn btn-default" ng-show="createMode" ng-click="logout()"><i class="glyphicon glyphicon-home"></i></a>
               <a id="logout" class="btn btn-default" ng-show="!createMode" ng-click="logout()"><i class="glyphicon glyphicon-off"style="color:red"></i></a>
               <!--add class breadcrumb-disabled-link to buttons to prevent click-->              
               <a ng-repeat="(key, value) in wizard" ui-sref-active="active" ui-sref=".{{key}}" class="btn btn-default" 

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/passwordreset.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/passwordreset.html b/client/enduser/src/main/resources/META-INF/resources/app/views/passwordreset.html
index 6c0f8e1..118b0d4 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/passwordreset.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/passwordreset.html
@@ -29,7 +29,7 @@ under the License.
 
           <div class="row">            
             <div id="status-buttons" class="btn-group btn-breadcrumb">
-              <a href="#/self" class="btn btn-default" ng-click="logout()"><i class="glyphicon glyphicon-home"></i></a>
+              <a class="btn btn-default" ng-click="logout()"><i class="glyphicon glyphicon-home"></i></a>
               <!--add class breadcrumb-disabled-link to buttons to prevent click-->
               <a ui-sref-active="active" class="btn btn-default">{{'USERDETAILS'| translate}}</a>
             </div>
@@ -63,7 +63,7 @@ under the License.
               </div>
               <button id="resetpassword" type="submit" class="btn btn-default pull-right">{{'SUBMIT'| translate}}</button>
               <div class="pull-left">
-                <a id="cancel" href="#/self" class="btn btn-danger" ng-click="logout()">{{'CANCEL'| translate}}</a>
+                <a id="cancel" class="btn btn-danger" ng-click="logout()">{{'CANCEL'| translate}}</a>
               </div>
             </div>
           </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
index 75f7251..71c2d01 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
@@ -26,7 +26,7 @@ under the License.
   </head>
   <body>
     <div ng-cloak class="container">
-      <div id="login-container" ng-controller="LoginController"  ng-init="init" style="box-sizing: border-box; ">
+      <div id="login-container" ng-controller="LoginController" style="box-sizing: border-box;">
         <div id="login" class="card card-container">
           <div>
             <img class="login-logo" src="img/logo-green.png" />
@@ -43,11 +43,20 @@ under the License.
                          ng-required ng-model="credentials.password">
                 </div>
                 <div class="form-group" ng-controller="UserController">
-                  <select  ng-change="switchLanguage()" id="language" style="width: 100%" class="btn dropdown-toggle btn-default" 
+                  <select  ng-change="switchLanguage()"
+                           id="language" style="width: 100%; text-align: left;" class="btn dropdown-toggle btn-default" 
                            ng-options="language.name for language in languages.availableLanguages track by language.id" 
                            ng-model="languages.selectedLanguage"></select>
                 </div>                
                 <div class="form-group">
+                  <select id="saml2sp" style="width: 100%; text-align: left;" class="btn dropdown-toggle btn-default"
+                          ng-if="$root.saml2spExtAvailable()"
+                          ng-options="idp.name for idp in saml2idps.available track by idp.entityID"
+                          ng-model="saml2idps.selected" ng-change="saml2login()">
+                    <option value="" disabled="disabled" selected="selected" hidden="hidden">SAML 2.0</option>
+                  </select>
+                </div>
+                <div class="form-group">
                   <button type="submit" id="login-btn" class="btn btn-default btn-signin login-btn" 
                           ng-click="login(credentials)">Login</button>
                 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
index 39f02bf..274e4bd 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
@@ -72,6 +72,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-derived-schemas.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-derived-schemas.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-derived-schemas.html
index abbe2c9..5836742 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-derived-schemas.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-derived-schemas.html
@@ -31,6 +31,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-form-finish.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-form-finish.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-form-finish.html
index f941235..8e58f23 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-form-finish.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-form-finish.html
@@ -32,6 +32,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
index 33c554a..70c3030 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
@@ -44,6 +44,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-plain-schemas.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-plain-schemas.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-plain-schemas.html
index 6bae9b4..b7d93eb 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-plain-schemas.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-plain-schemas.html
@@ -31,6 +31,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-resources.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-resources.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-resources.html
index c030be8..86ee3bd 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-resources.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-resources.html
@@ -31,6 +31,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/client/enduser/src/main/resources/META-INF/resources/app/views/user-virtual-schemas.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-virtual-schemas.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-virtual-schemas.html
index ceb6b78..81e4e78 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-virtual-schemas.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-virtual-schemas.html
@@ -31,6 +31,6 @@ under the License.
     </div>
   </div>
   <div class="pull-left">
-    <a id="cancel" href="#/self" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
+    <a id="cancel" class="btn btn-danger pull-left" ng-click="logout()">{{'CANCEL'| translate}}</a>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
index eaea49f..00a853e 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.ext.saml2lsp.agent;
 
 import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
@@ -66,7 +68,8 @@ public class AssertionConsumer extends HttpServlet {
 
                 e.printStackTrace(response.getWriter());
             } else {
-                response.sendRedirect(errorURL + "?errorMessage=" + e.getMessage());
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
index a04376d..a675ca7 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.ext.saml2lsp.agent;
 
 import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -56,7 +58,8 @@ public class Login extends SAML2PostBinding {
 
                 e.printStackTrace(response.getWriter());
             } else {
-                response.sendRedirect(errorURL + "?errorMessage=" + e.getMessage());
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
index ec59499..df4c5e5 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.ext.saml2lsp.agent;
 
 import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -61,7 +63,8 @@ public class Logout extends SAML2PostBinding {
 
                 e.printStackTrace(response.getWriter());
             } else {
-                response.sendRedirect(errorURL + "?errorMessage=" + e.getMessage());
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
             }
         }
     }
@@ -98,7 +101,8 @@ public class Logout extends SAML2PostBinding {
 
                 e.printStackTrace(response.getWriter());
             } else {
-                response.sendRedirect(errorURL + "?errorMessage=" + e.getMessage());
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/pages/SAML2SPLogin.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/pages/SAML2SPLogin.java b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/pages/SAML2SPLogin.java
index 6b975cb..69f8294 100644
--- a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/pages/SAML2SPLogin.java
+++ b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/pages/SAML2SPLogin.java
@@ -33,6 +33,8 @@ public class SAML2SPLogin extends WebPage {
 
     private static final Logger LOG = LoggerFactory.getLogger(SAML2SPLogin.class);
 
+    private static final String SAML_ACCESS_ERROR = "SAML 2.0 access error";
+
     public SAML2SPLogin(final PageParameters parameters) {
         super(parameters);
 
@@ -42,7 +44,7 @@ public class SAML2SPLogin extends WebPage {
             LOG.error("No JWT found, redirecting to default greeter");
 
             PageParameters params = new PageParameters();
-            params.add("errorMessage", "accessControlException");
+            params.add("errorMessage", SAML_ACCESS_ERROR);
             setResponsePage(Login.class, params);
         }
 
@@ -61,7 +63,7 @@ public class SAML2SPLogin extends WebPage {
             setResponsePage(getApplication().getHomePage());
         } else {
             PageParameters params = new PageParameters();
-            params.add("errorMessage", "accessControlException");
+            params.add("errorMessage", SAML_ACCESS_ERROR);
             setResponsePage(Login.class, params);
         }
         strategy.remove();

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPBeforeLogout.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPBeforeLogout.java b/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPBeforeLogout.java
new file mode 100644
index 0000000..eecfba8
--- /dev/null
+++ b/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPBeforeLogout.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.request.UrlUtils;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.handler.RedirectRequestHandler;
+
+public class SAML2SPBeforeLogout extends WebPage {
+
+    private static final long serialVersionUID = 3727183730959367707L;
+
+    public SAML2SPBeforeLogout() {
+        super();
+
+        RequestCycle.get().scheduleRequestHandlerAfterCurrent(new RedirectRequestHandler(
+                UrlUtils.rewriteToContextRelative("saml2sp/logout", RequestCycle.get())));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/14774113/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPLogin.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPLogin.java b/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPLogin.java
new file mode 100644
index 0000000..66739ee
--- /dev/null
+++ b/ext/saml2sp/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/SAML2SPLogin.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.Constants;
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SAML2SPLogin extends WebPage {
+
+    private static final long serialVersionUID = 8581614051773949262L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(SAML2SPLogin.class);
+
+    private static final String SAML_ACCESS_ERROR = "SAML 2.0 access error";
+
+    public SAML2SPLogin(final PageParameters parameters) {
+        super(parameters);
+
+        String token = (String) ((ServletWebRequest) getRequest()).getContainerRequest().
+                getSession().getAttribute(org.apache.syncope.ext.saml2lsp.agent.Constants.SAML2SPJWT);
+        if (StringUtils.isBlank(token)) {
+            LOG.error("No JWT found, redirecting to default greeter");
+
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", SAML_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+
+        IAuthenticationStrategy strategy = getApplication().getSecuritySettings().getAuthenticationStrategy();
+
+        if (SyncopeEnduserSession.get().authenticate(token)) {
+            if (parameters.get("sloSupported").toBoolean(false)) {
+                SyncopeEnduserSession.get().setAttribute(Constants.BEFORE_LOGOUT, SAML2SPBeforeLogout.class);
+            }
+
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", SAML_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+        strategy.remove();
+    }
+}