You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2018/07/23 16:03:40 UTC

[cxf-fediz] 01/02: FEDIZ-222 - Implemented SAML LogoutResponse logic in the plugin core

This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit 922420b2a05647cf8669b26e35547736f74bb921
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Jul 23 16:33:40 2018 +0100

    FEDIZ-222 - Implemented SAML LogoutResponse logic in the plugin core
---
 .../cxf/fediz/core/handler/LogoutHandler.java      |  69 ++++-
 .../core/processor/AbstractFedizProcessor.java     |  21 +-
 .../cxf/fediz/core/processor/FedizRequest.java     |  10 +-
 .../fediz/core/processor/SAMLProcessorImpl.java    |  12 +-
 .../apache/cxf/fediz/core/util/StringUtils.java    |  23 ++
 .../core/federation/FederationLogoutTest.java      |  15 +
 .../cxf/fediz/core/samlsso/SAMLLogoutTest.java     | 333 +++++++++++++++++++++
 .../cxf/fediz/core/samlsso/SAMLResponseTest.java   |  37 +--
 .../src/test/resources/fediz_test_config_saml.xml  |   7 +-
 9 files changed, 477 insertions(+), 50 deletions(-)

diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
index c9fe079..e6a5f17 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
@@ -36,10 +36,13 @@ import org.w3c.dom.Element;
 
 import org.apache.cxf.fediz.core.FederationConstants;
 import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.exception.ProcessingException;
 import org.apache.cxf.fediz.core.processor.FedizProcessor;
 import org.apache.cxf.fediz.core.processor.FedizProcessorFactory;
+import org.apache.cxf.fediz.core.processor.FedizRequest;
 import org.apache.cxf.fediz.core.processor.RedirectionResponse;
 import org.apache.cxf.fediz.core.spi.ReplyConstraintCallback;
+import org.apache.cxf.fediz.core.util.StringUtils;
 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,6 +83,8 @@ public class LogoutHandler implements RequestHandler<Boolean> {
             return signout(request, response);
         } else if (FederationConstants.ACTION_SIGNOUT_CLEANUP.equals(wa)) {
             return signoutCleanup(request, response);
+        } else if (request.getParameter("SAMLResponse") != null) {
+            return handleSAMLSSOLogoutResponse(request, response);
         } else {
             return customLogout(request, response);
         }
@@ -95,7 +100,7 @@ public class LogoutHandler implements RequestHandler<Boolean> {
         request.getSession().invalidate();
 
         String wreply = request.getParameter(FederationConstants.PARAM_REPLY);
-
+        String logoutRedirectTo = fedizConfig.getLogoutRedirectTo();
         if (wreply != null && !wreply.isEmpty()) {
             Pattern logoutRedirectToConstraint = null;
             try {
@@ -120,6 +125,21 @@ public class LogoutHandler implements RequestHandler<Boolean> {
                              wreply, logoutRedirectToConstraint);
                 }
             }
+        } else if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) {
+            try {
+                if (logoutRedirectTo.startsWith("/")) {
+                    logoutRedirectTo =
+                        StringUtils.extractFullContextPath(request).concat(logoutRedirectTo.substring(1));
+                } else if (!logoutRedirectTo.startsWith("http") && !logoutRedirectTo.startsWith("https")) {
+                    logoutRedirectTo = StringUtils.extractFullContextPath(request).concat(logoutRedirectTo);
+                }
+
+                LOG.debug("Redirecting after logout to={}", logoutRedirectTo);
+                response.sendRedirect(response.encodeRedirectURL(logoutRedirectTo));
+                return true;
+            } catch (Exception e) {
+                LOG.error("Error redirecting user after logout: {}", e.getMessage());
+            }
         }
 
         writeLogoutImage(response);
@@ -204,4 +224,51 @@ public class LogoutHandler implements RequestHandler<Boolean> {
         }
     }
 
+    protected boolean handleSAMLSSOLogoutResponse(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            FedizProcessor wfProc = FedizProcessorFactory.newFedizProcessor(fedizConfig.getProtocol());
+
+            FedizRequest wfReq = new FedizRequest();
+            wfReq.setResponseToken(request.getParameter("SAMLResponse"));
+            wfReq.setState(request.getParameter("RelayState"));
+            wfReq.setRequest(request);
+            wfReq.setSignOutResponse(true);
+
+            wfProc.processRequest(wfReq, fedizConfig);
+
+            String logoutRedirectTo = fedizConfig.getLogoutRedirectTo();
+            if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) {
+                try {
+                    if (logoutRedirectTo.startsWith("/")) {
+                        logoutRedirectTo =
+                            StringUtils.extractFullContextPath(request).concat(logoutRedirectTo.substring(1));
+                    } else if (!logoutRedirectTo.startsWith("http") && !logoutRedirectTo.startsWith("https")) {
+                        logoutRedirectTo = StringUtils.extractFullContextPath(request).concat(logoutRedirectTo);
+                    }
+
+                    LOG.debug("Redirecting after logout to={}", logoutRedirectTo);
+                    response.sendRedirect(response.encodeRedirectURL(logoutRedirectTo));
+                } catch (Exception e) {
+                    LOG.error("Error redirecting user after logout: {}", e.getMessage());
+                }
+            } else {
+                writeLogoutImage(response);
+            }
+            return true;
+        } catch (ProcessingException ex) {
+            LOG.warn("Failed to validate SAMLResponse: " + ex.getMessage());
+            handleSAMLResponseValidationError(ex, response);
+            return false;
+        }
+    }
+
+    // Allow the user to handle some custom logic if the SAMLResponse validation fails for logout
+    protected void handleSAMLResponseValidationError(ProcessingException ex, HttpServletResponse response) {
+        try {
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to validate SAMLResponse.");
+        } catch (IOException e) {
+            LOG.error("Failed to send error response: {}", e.getMessage());
+        }
+    }
+
 }
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
index f7900cb..c9821ca 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
@@ -21,7 +21,6 @@ package org.apache.cxf.fediz.core.processor;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
-import java.net.URL;
 import java.time.Instant;
 
 import javax.security.auth.callback.Callback;
@@ -34,6 +33,7 @@ import org.apache.cxf.fediz.core.exception.ProcessingException;
 import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
 import org.apache.cxf.fediz.core.spi.IDPCallback;
 import org.apache.cxf.fediz.core.spi.RealmCallback;
+import org.apache.cxf.fediz.core.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -99,24 +99,7 @@ public abstract class AbstractFedizProcessor implements FedizProcessor {
     }
 
     protected String extractFullContextPath(HttpServletRequest request) throws MalformedURLException {
-        String result = null;
-        String contextPath = request.getContextPath();
-        String requestUrl = request.getRequestURL().toString();
-        String requestPath = new URL(requestUrl).getPath();
-        // Cut request path of request url and add context path if not ROOT
-        if (requestPath != null && requestPath.length() > 0) {
-            int lastIndex = requestUrl.lastIndexOf(requestPath);
-            result = requestUrl.substring(0, lastIndex);
-        } else {
-            result = requestUrl;
-        }
-        if (contextPath != null && contextPath.length() > 0) {
-            // contextPath contains starting slash
-            result = result + contextPath + "/";
-        } else {
-            result = result + "/";
-        }
-        return result;
+        return StringUtils.extractFullContextPath(request);
     }
 
 }
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FedizRequest.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FedizRequest.java
index 03bb776..2e770a9 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FedizRequest.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FedizRequest.java
@@ -38,7 +38,7 @@ public class FedizRequest implements Serializable {
     private Certificate[] certs;
     private transient HttpServletRequest request;
     private RequestState requestState;
-    private boolean signOutRequest;
+    private boolean signOutResponse;
 
     public Certificate[] getCerts() {
         if (certs != null) {
@@ -89,11 +89,11 @@ public class FedizRequest implements Serializable {
     public void setRequestState(RequestState requestState) {
         this.requestState = requestState;
     }
-    public boolean isSignOutRequest() {
-        return signOutRequest;
+    public boolean isSignOutResponse() {
+        return signOutResponse;
     }
-    public void setSignOutRequest(boolean signOutRequest) {
-        this.signOutRequest = signOutRequest;
+    public void setSignOutResponse(boolean signOutResponse) {
+        this.signOutResponse = signOutResponse;
     }
 
 }
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
index 93020d7..78c4c26 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
@@ -98,7 +98,7 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor {
             throw new ProcessingException(TYPE.INVALID_REQUEST);
         }
 
-        if (request.isSignOutRequest()) {
+        if (request.isSignOutResponse()) {
             return processSignOutResponse(request, config);
         }
 
@@ -272,20 +272,20 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor {
             throw new ProcessingException(TYPE.INVALID_REQUEST);
         }
 
-        org.opensaml.saml.saml2.core.LogoutResponse logoutResponse = 
+        org.opensaml.saml.saml2.core.LogoutResponse logoutResponse =
             (org.opensaml.saml.saml2.core.LogoutResponse)responseObject;
-        
+
         // Validate the Response
         validateSamlResponseProtocol(logoutResponse, config);
-        
+
         // Enforce that the LogoutResponse is signed - we don't support a separate signature for now
         if (!logoutResponse.isSigned()) {
             LOG.debug("The LogoutResponse is not signed");
             throw new ProcessingException(TYPE.INVALID_REQUEST);
         }
-        
+
         Instant issueInstant = logoutResponse.getIssueInstant().toDate().toInstant();
-        
+
         FedizResponse fedResponse = new FedizResponse(
             null, logoutResponse.getIssuer().getValue(),
             Collections.emptyList(), Collections.emptyList(),
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/util/StringUtils.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/util/StringUtils.java
index 62d9e88..4c13434 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/util/StringUtils.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/util/StringUtils.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Few simple utils. This is originally from the CXF project.
  */
@@ -230,4 +232,25 @@ public final class StringUtils {
                 .append(Character.toLowerCase(str.charAt(0)))
                 .append(str.substring(1)).toString();
     }
+
+    public static String extractFullContextPath(HttpServletRequest request) throws MalformedURLException {
+        String result = null;
+        String contextPath = request.getContextPath();
+        String requestUrl = request.getRequestURL().toString();
+        String requestPath = new URL(requestUrl).getPath();
+        // Cut request path of request url and add context path if not ROOT
+        if (requestPath != null && requestPath.length() > 0) {
+            int lastIndex = requestUrl.lastIndexOf(requestPath);
+            result = requestUrl.substring(0, lastIndex);
+        } else {
+            result = requestUrl;
+        }
+        if (contextPath != null && contextPath.length() > 0) {
+            // contextPath contains starting slash
+            result = result + contextPath + "/";
+        } else {
+            result = result + "/";
+        }
+        return result;
+    }
 }
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
index e40f861..4f10ebe 100644
--- a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
@@ -95,6 +95,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -120,6 +121,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -145,6 +147,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -170,6 +173,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -195,6 +199,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -220,6 +225,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -245,6 +251,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
@@ -271,6 +278,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
             .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
         EasyMock.expect(req.getRequestURI()).andReturn("/secure");
         EasyMock.expect(req.getContextPath()).andReturn("/secure");
@@ -297,6 +305,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
             .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
         EasyMock.expect(req.getRequestURI()).andReturn("/secure");
         EasyMock.expect(req.getContextPath()).andReturn("/secure");
@@ -323,6 +332,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
             .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
         EasyMock.expect(req.getRequestURI()).andReturn("/secure");
         EasyMock.expect(req.getContextPath()).andReturn("/secure");
@@ -349,6 +359,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
         .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
         EasyMock.expect(req.getRequestURI()).andReturn("/secure");
         EasyMock.expect(req.getContextPath()).andReturn("/secure");
@@ -377,6 +388,7 @@ public class FederationLogoutTest {
             .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
         EasyMock.expect(req.getSession()).andReturn(session);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.replay(req);
 
         LogoutHandler logoutHandler = new LogoutHandler(config);
@@ -401,6 +413,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
             .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
         EasyMock.expect(req.getSession()).andReturn(session);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
         EasyMock.replay(req);
 
@@ -425,6 +438,7 @@ public class FederationLogoutTest {
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
             .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
         EasyMock.expect(req.getSession()).andReturn(session);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
         EasyMock.replay(req);
 
@@ -447,6 +461,7 @@ public class FederationLogoutTest {
         HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
         EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(null);
         EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
         EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
         EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLLogoutTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLLogoutTest.java
new file mode 100644
index 0000000..abd3c5f
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLLogoutTest.java
@@ -0,0 +1,333 @@
+/**
+ * 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.cxf.fediz.core.samlsso;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.UUID;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.cxf.fediz.common.SecurityTestUtil;
+import org.apache.cxf.fediz.core.KeystoreCallbackHandler;
+import org.apache.cxf.fediz.core.config.FedizConfigurator;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.handler.LogoutHandler;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.ext.WSPasswordCallback;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.util.DOM2Writer;
+import org.easymock.EasyMock;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.opensaml.saml.common.SAMLObjectContentReference;
+import org.opensaml.saml.common.SignableSAMLObject;
+import org.opensaml.saml.saml2.core.LogoutResponse;
+import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Some tests for logout for SAML SSO
+ */
+public class SAMLLogoutTest {
+    private static final String LOGOUT_URL = "https://localhost/fedizhelloworld/secure/logout";
+    private static final String LOGOUT_URI = "/secure/logout";
+    static final String TEST_REQUEST_URL = "https://localhost/fedizhelloworld/";
+    static final String TEST_IDP_ISSUER = "http://url_to_the_issuer";
+    static final String TEST_CLIENT_ADDRESS = "https://127.0.0.1";
+
+    private static final String CONFIG_FILE = "fediz_test_config_saml.xml";
+
+    private static Crypto crypto;
+    private static CallbackHandler cbPasswordHandler;
+    private static FedizConfigurator configurator;
+    private static DocumentBuilderFactory docBuilderFactory;
+
+    static {
+        OpenSAMLUtil.initSamlEngine();
+        docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+    }
+
+
+    @BeforeClass
+    public static void init() {
+        try {
+            crypto = CryptoFactory.getInstance("signature.properties");
+            cbPasswordHandler = new KeystoreCallbackHandler();
+            getFederationConfigurator();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        Assert.assertNotNull(configurator);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        SecurityTestUtil.cleanup();
+    }
+
+
+    private static FedizConfigurator getFederationConfigurator() {
+        if (configurator != null) {
+            return configurator;
+        }
+        try {
+            configurator = new FedizConfigurator();
+            final URL resource = Thread.currentThread().getContextClassLoader()
+                    .getResource(CONFIG_FILE);
+            File f = new File(resource.toURI());
+            configurator.loadConfig(f);
+            return configurator;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @org.junit.Test
+    public void testLogoutResponse() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
+        Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, true, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(encodeResponse(logoutResponse)).anyTimes();
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        EasyMock.expect(req.getParameter("RelayState")).andReturn(relayState);
+        EasyMock.expect(req.getParameter("wa")).andReturn(null).times(2);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedLogoutRedirect = "https://localhost/secure/logout/redir.html";
+        EasyMock.expect(resp.encodeRedirectURL(expectedLogoutRedirect)).andReturn(expectedLogoutRedirect);
+        resp.sendRedirect(expectedLogoutRedirect);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+
+    @org.junit.Test
+    public void testUnsignedLogoutResponse() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
+        Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, false, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(encodeResponse(logoutResponse)).anyTimes();
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        EasyMock.expect(req.getParameter("RelayState")).andReturn(relayState);
+        EasyMock.expect(req.getParameter("wa")).andReturn(null).times(2);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to validate SAMLResponse.");
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+
+    @org.junit.Test
+    public void testUntrustedLogoutResponse() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("CLIENT_TRUST");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
+        Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, false, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(encodeResponse(logoutResponse)).anyTimes();
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        EasyMock.expect(req.getParameter("RelayState")).andReturn(relayState);
+        EasyMock.expect(req.getParameter("wa")).andReturn(null).times(2);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to validate SAMLResponse.");
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+
+    @org.junit.Test
+    public void validateBadStatusInLogoutResponse() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String status = "urn:oasis:names:tc:SAML:2.0:status:Requester";
+        Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, false, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter("SAMLResponse")).andReturn(encodeResponse(logoutResponse)).anyTimes();
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        EasyMock.expect(req.getParameter("RelayState")).andReturn(relayState);
+        EasyMock.expect(req.getParameter("wa")).andReturn(null).times(2);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to validate SAMLResponse.");
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+
+
+    private Element createLogoutResponse(String statusValue, String destination,
+                                         boolean sign, String requestID) throws Exception {
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Status status =
+            SAML2PResponseComponentBuilder.createStatus(statusValue, null);
+        LogoutResponse response =
+            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, TEST_IDP_ISSUER, status, destination);
+
+        // Sign the LogoutResponse
+        if (sign) {
+            signResponse(response, "mystskey");
+        }
+
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+
+        return policyElement;
+    }
+
+    private void signResponse(SignableSAMLObject signableObject, String alias) throws Exception {
+
+        Signature signature = OpenSAMLUtil.buildSignature();
+        signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(alias);
+        X509Certificate[] issuerCerts = crypto.getX509Certificates(cryptoType);
+
+        String sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
+        } else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1;
+        }
+
+        WSPasswordCallback[] cb = {
+            new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)
+        };
+        cbPasswordHandler.handle(cb);
+        String password = cb[0].getPassword();
+
+        PrivateKey privateKey;
+        try {
+            privateKey = crypto.getPrivateKey(alias, password);
+        } catch (Exception ex) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
+        }
+        if (privateKey == null) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
+                new Object[] {"No private key was found using issuer name: " + alias});
+        }
+
+        signature.setSignatureAlgorithm(sigAlgo);
+
+        BasicX509Credential signingCredential =
+            new BasicX509Credential(issuerCerts[0], privateKey);
+
+        signature.setSigningCredential(signingCredential);
+
+        X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
+        kiFactory.setEmitEntityCertificate(true);
+
+        try {
+            KeyInfo keyInfo = kiFactory.newInstance().generate(signingCredential);
+            signature.setKeyInfo(keyInfo);
+        } catch (org.opensaml.security.SecurityException ex) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
+                new Object[] {"Error generating KeyInfo from signing credential"});
+        }
+
+        signableObject.setSignature(signature);
+        String digestAlg = SignatureConstants.ALGO_ID_DIGEST_SHA1;
+        SAMLObjectContentReference contentRef =
+            (SAMLObjectContentReference)signature.getContentReferences().get(0);
+        contentRef.setDigestAlgorithm(digestAlg);
+        signableObject.releaseDOM();
+        signableObject.releaseChildrenDOM(true);
+    }
+
+    private String encodeResponse(Element response) throws IOException {
+        String responseMessage = DOM2Writer.nodeToString(response);
+
+        byte[] deflatedBytes = CompressionUtils.deflate(responseMessage.getBytes(StandardCharsets.UTF_8));
+
+        return Base64.getEncoder().encodeToString(deflatedBytes);
+    }
+
+}
\ No newline at end of file
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
index 69fd12e..63e5611 100644
--- a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
@@ -116,6 +116,7 @@ public class SAMLResponseTest {
     private static DocumentBuilderFactory docBuilderFactory;
 
     static {
+        OpenSAMLUtil.initSamlEngine();
         docBuilderFactory = DocumentBuilderFactory.newInstance();
         docBuilderFactory.setNamespaceAware(true);
     }
@@ -1250,14 +1251,14 @@ public class SAMLResponseTest {
             // expected
         }
     }
-    
+
     @org.junit.Test
     public void validateLogoutResponse() throws Exception {
         // Mock up a LogoutResponse
         FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
 
         String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
-        
+
         String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
         Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, true, requestId);
 
@@ -1271,19 +1272,19 @@ public class SAMLResponseTest {
         String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
         wfReq.setState(relayState);
         wfReq.setRequest(req);
-        wfReq.setSignOutRequest(true);
+        wfReq.setSignOutResponse(true);
 
         FedizProcessor wfProc = new SAMLProcessorImpl();
         wfProc.processRequest(wfReq, config);
     }
-    
+
     @org.junit.Test
     public void validateUnsignedLogoutResponse() throws Exception {
         // Mock up a LogoutResponse
         FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
 
         String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
-        
+
         String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
         Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, false, requestId);
 
@@ -1297,7 +1298,7 @@ public class SAMLResponseTest {
         String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
         wfReq.setState(relayState);
         wfReq.setRequest(req);
-        wfReq.setSignOutRequest(true);
+        wfReq.setSignOutResponse(true);
 
         FedizProcessor wfProc = new SAMLProcessorImpl();
         try {
@@ -1307,14 +1308,14 @@ public class SAMLResponseTest {
             // expected
         }
     }
-    
+
     @org.junit.Test
     public void validateUntrustedLogoutResponse() throws Exception {
         // Mock up a LogoutResponse
         FedizContext config = getFederationConfigurator().getFedizContext("CLIENT_TRUST");
 
         String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
-        
+
         String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
         Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, true, requestId);
 
@@ -1328,7 +1329,7 @@ public class SAMLResponseTest {
         String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
         wfReq.setState(relayState);
         wfReq.setRequest(req);
-        wfReq.setSignOutRequest(true);
+        wfReq.setSignOutResponse(true);
 
         FedizProcessor wfProc = new SAMLProcessorImpl();
         try {
@@ -1338,14 +1339,14 @@ public class SAMLResponseTest {
             // expected
         }
     }
-    
+
     @org.junit.Test
     public void validateBadStatusInLogoutResponse() throws Exception {
         // Mock up a LogoutResponse
         FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
 
         String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
-        
+
         String status = "urn:oasis:names:tc:SAML:2.0:status:Requester";
         Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL, true, requestId);
 
@@ -1359,7 +1360,7 @@ public class SAMLResponseTest {
         String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
         wfReq.setState(relayState);
         wfReq.setRequest(req);
-        wfReq.setSignOutRequest(true);
+        wfReq.setSignOutResponse(true);
 
         FedizProcessor wfProc = new SAMLProcessorImpl();
         try {
@@ -1376,7 +1377,7 @@ public class SAMLResponseTest {
         FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
 
         String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
-        
+
         String status = "urn:oasis:names:tc:SAML:2.0:status:Success";
         Element logoutResponse = createLogoutResponse(status, TEST_REQUEST_URL + "_", false, requestId);
 
@@ -1390,7 +1391,7 @@ public class SAMLResponseTest {
         String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
         wfReq.setState(relayState);
         wfReq.setRequest(req);
-        wfReq.setSignOutRequest(true);
+        wfReq.setSignOutResponse(true);
 
         FedizProcessor wfProc = new SAMLProcessorImpl();
         try {
@@ -1469,7 +1470,7 @@ public class SAMLResponseTest {
         return policyElement;
     }
 
-    private Element createLogoutResponse(String statusValue, String destination, 
+    private Element createLogoutResponse(String statusValue, String destination,
                                          boolean sign, String requestID) throws Exception {
         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
         Document doc = docBuilder.newDocument();
@@ -1489,7 +1490,7 @@ public class SAMLResponseTest {
 
         return policyElement;
     }
-    
+
     private void signResponse(SignableSAMLObject signableObject, String alias) throws Exception {
 
         Signature signature = OpenSAMLUtil.buildSignature();
@@ -1505,13 +1506,13 @@ public class SAMLResponseTest {
         } else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
             sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1;
         }
-        
+
         WSPasswordCallback[] cb = {
             new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)
         };
         cbPasswordHandler.handle(cb);
         String password = cb[0].getPassword();
-        
+
         PrivateKey privateKey;
         try {
             privateKey = crypto.getPrivateKey(alias, password);
diff --git a/plugins/core/src/test/resources/fediz_test_config_saml.xml b/plugins/core/src/test/resources/fediz_test_config_saml.xml
index c247351..53adc67 100644
--- a/plugins/core/src/test/resources/fediz_test_config_saml.xml
+++ b/plugins/core/src/test/resources/fediz_test_config_saml.xml
@@ -42,6 +42,9 @@
 				<claimType type="a particular claim type" optional="true" />
 			</claimTypesRequested>
 		</protocol>
+		
+		<logoutURL>secure/logout</logoutURL>
+        <logoutRedirectTo>/redir.html</logoutRedirectTo>
 	</contextConfig>
 	
 	<contextConfig name="ROOT2">
@@ -236,6 +239,8 @@
 				<claimType type="a particular claim type" optional="true" />
 			</claimTypesRequested>
 		</protocol>
-	</contextConfig>
 	
+		<logoutURL>secure/logout</logoutURL>
+   	 	<logoutRedirectTo>/redir.html</logoutRedirectTo>
+   	 </contextConfig>
 </FedizConfig>