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/16 16:52:13 UTC

[cxf-fediz] 02/02: FEDIZ-221 - Sign the LogoutResponses from the IdP

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 f934237c80eb2d7b24a63da7d8ce8cfa7cbc14a9
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Jul 16 17:51:46 2018 +0100

    FEDIZ-221 - Sign the LogoutResponses from the IdP
---
 .../beans/samlsso/AbstractSamlResponseCreator.java | 176 +++++++++++++++++++++
 .../idp/beans/samlsso/SamlResponseCreator.java     |  62 +-------
 .../beans/samlsso/SamlResponseErrorCreator.java    |  56 +------
 .../apache/cxf/fediz/systests/samlsso/IdpTest.java |  39 +++--
 4 files changed, 204 insertions(+), 129 deletions(-)

diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AbstractSamlResponseCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AbstractSamlResponseCreator.java
new file mode 100644
index 0000000..0e9c802
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AbstractSamlResponseCreator.java
@@ -0,0 +1,176 @@
+/**
+ * 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.service.idp.beans.samlsso;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.fediz.core.util.CertsUtils;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.util.DOM2Writer;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractSamlResponseCreator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSamlResponseCreator.class);
+    private boolean signLogoutResponse = true;
+    private boolean supportDeflateEncoding;
+    private boolean useRealmForIssuer;
+
+    protected Element createLogoutResponse(Idp idp, String statusValue,
+                                           String destination, String requestID) throws Exception {
+        Document doc = DOMUtils.newDocument();
+
+        Status status =
+            SAML2PResponseComponentBuilder.createStatus(statusValue, null);
+        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
+        LogoutResponse response =
+            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination);
+
+        // Sign the LogoutResponse
+        signResponse(response, idp);
+
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+
+        return policyElement;
+    }
+
+    protected void signResponse(SignableSAMLObject signableObject, Idp idp) throws Exception {
+        if (!signLogoutResponse) {
+            return;
+        }
+        Crypto issuerCrypto = CertsUtils.getCryptoFromCertificate(idp.getCertificate());
+        String issuerKeyName = issuerCrypto.getDefaultX509Identifier();
+        String issuerKeyPassword = idp.getCertificatePassword();
+
+        Signature signature = OpenSAMLUtil.buildSignature();
+        signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(issuerKeyName);
+        X509Certificate[] issuerCerts = null;
+        if (issuerCrypto != null) {
+            issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
+        }
+        if (issuerCerts == null || issuerCerts.length == 0) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
+                new Object[] {"No issuer certs were found to sign the SAML Assertion using issuer name: "
+                                              + issuerKeyName});
+        }
+
+        String sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+        LOG.debug("automatic sig algo detection: {}", pubKeyAlgo);
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
+        } else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1;
+        }
+        LOG.debug("Using Signature algorithm {}", sigAlgo);
+        PrivateKey privateKey;
+        try {
+            privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
+        } 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: " + issuerKeyName});
+        }
+
+        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);
+    }
+
+    protected String encodeResponse(Element response) throws IOException {
+        String responseMessage = DOM2Writer.nodeToString(response);
+        LOG.debug("Created Response: {}", responseMessage);
+
+        if (supportDeflateEncoding) {
+            DeflateEncoderDecoder encoder = new DeflateEncoderDecoder();
+            byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes(StandardCharsets.UTF_8));
+
+            return Base64Utility.encode(deflatedBytes);
+        }
+
+        return Base64Utility.encode(responseMessage.getBytes());
+    }
+
+    public boolean isSupportDeflateEncoding() {
+        return supportDeflateEncoding;
+    }
+
+    public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
+        this.supportDeflateEncoding = supportDeflateEncoding;
+    }
+
+    public boolean isUseRealmForIssuer() {
+        return useRealmForIssuer;
+    }
+
+    public void setUseRealmForIssuer(boolean useRealmForIssuer) {
+        this.useRealmForIssuer = useRealmForIssuer;
+    }
+}
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
index 426bbf7..b91544d 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
@@ -18,15 +18,12 @@
  */
 package org.apache.cxf.fediz.service.idp.beans.samlsso;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.cxf.common.util.Base64Utility;
 import org.apache.cxf.fediz.core.exception.ProcessingException;
 import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
 import org.apache.cxf.fediz.core.util.CertsUtils;
@@ -37,7 +34,6 @@ import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder;
 import org.apache.cxf.fediz.service.idp.samlsso.SAMLAuthnRequest;
 import org.apache.cxf.fediz.service.idp.util.WebUtils;
 import org.apache.cxf.helpers.DOMUtils;
-import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
 import org.apache.wss4j.common.crypto.Crypto;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
 import org.apache.wss4j.common.saml.SAMLCallback;
@@ -46,11 +42,9 @@ import org.apache.wss4j.common.saml.SamlAssertionWrapper;
 import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
 import org.apache.wss4j.common.saml.bean.ConditionsBean;
 import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
-import org.apache.wss4j.common.util.DOM2Writer;
 import org.apache.wss4j.dom.WSConstants;
 import org.joda.time.DateTime;
 import org.opensaml.saml.saml2.core.Assertion;
-import org.opensaml.saml.saml2.core.LogoutResponse;
 import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.Status;
@@ -63,11 +57,9 @@ import org.springframework.webflow.execution.RequestContext;
  * Insert the SAML Token received from the STS into a SAML Response
  */
 @Component
-public class SamlResponseCreator {
+public class SamlResponseCreator extends AbstractSamlResponseCreator {
 
     private static final Logger LOG = LoggerFactory.getLogger(SamlResponseCreator.class);
-    private boolean supportDeflateEncoding;
-    private boolean useRealmForIssuer;
 
     public String createSAMLResponse(RequestContext context, Idp idp, Element rpToken,
                                      String consumerURL, String requestId, String requestIssuer)
@@ -100,7 +92,8 @@ public class SamlResponseCreator {
     public String createSAMLLogoutResponse(RequestContext context, Idp idp, String destination, String requestId)
                                          throws ProcessingException {
         try {
-            Element response = createLogoutResponse(idp, destination, requestId);
+            Element response = createLogoutResponse(idp, "urn:oasis:names:tc:SAML:2.0:status:Success",
+                                                    destination, requestId);
             return encodeResponse(response);
         } catch (Exception ex) {
             LOG.warn("Error marshalling SAML Token: {}", ex.getMessage());
@@ -113,7 +106,7 @@ public class SamlResponseCreator {
                                            String remoteAddr, String racs) throws Exception {
         // Create an AuthenticationAssertion
         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
-        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
+        String issuer = isUseRealmForIssuer() ? idp.getRealm() : idp.getIdpUrl().toString();
         callbackHandler.setIssuer(issuer);
         callbackHandler.setSubject(receivedToken.getSaml2().getSubject());
 
@@ -167,7 +160,7 @@ public class SamlResponseCreator {
             SAML2PResponseComponentBuilder.createStatus(
                 "urn:oasis:names:tc:SAML:2.0:status:Success", null
             );
-        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
+        String issuer = isUseRealmForIssuer() ? idp.getRealm() : idp.getIdpUrl().toString();
         Response response =
             SAML2PResponseComponentBuilder.createSAMLResponse(requestID, issuer, status);
 
@@ -179,50 +172,5 @@ public class SamlResponseCreator {
         return policyElement;
     }
 
-    protected Element createLogoutResponse(Idp idp, String destination, String requestID) throws Exception {
-        Document doc = DOMUtils.newDocument();
-
-        Status status =
-            SAML2PResponseComponentBuilder.createStatus(
-                "urn:oasis:names:tc:SAML:2.0:status:Success", null
-            );
-        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
-        LogoutResponse response =
-            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination);
-
-        Element policyElement = OpenSAMLUtil.toDom(response, doc);
-        doc.appendChild(policyElement);
-
-        return policyElement;
-    }
-
-    protected String encodeResponse(Element response) throws IOException {
-        String responseMessage = DOM2Writer.nodeToString(response);
-        LOG.debug("Created Response: {}", responseMessage);
 
-        if (supportDeflateEncoding) {
-            DeflateEncoderDecoder encoder = new DeflateEncoderDecoder();
-            byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes(StandardCharsets.UTF_8));
-
-            return Base64Utility.encode(deflatedBytes);
-        }
-
-        return Base64Utility.encode(responseMessage.getBytes());
-    }
-
-    public boolean isSupportDeflateEncoding() {
-        return supportDeflateEncoding;
-    }
-
-    public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
-        this.supportDeflateEncoding = supportDeflateEncoding;
-    }
-
-    public boolean isUseRealmForIssuer() {
-        return useRealmForIssuer;
-    }
-
-    public void setUseRealmForIssuer(boolean useRealmForIssuer) {
-        this.useRealmForIssuer = useRealmForIssuer;
-    }
 }
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
index d201d0a..f6996c2 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
@@ -18,22 +18,15 @@
  */
 package org.apache.cxf.fediz.service.idp.beans.samlsso;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.cxf.common.util.Base64Utility;
 import org.apache.cxf.fediz.core.exception.ProcessingException;
 import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
 import org.apache.cxf.fediz.service.idp.domain.Idp;
 import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder;
 import org.apache.cxf.helpers.DOMUtils;
-import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
-import org.apache.wss4j.common.util.DOM2Writer;
-import org.opensaml.saml.saml2.core.LogoutResponse;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.Status;
 import org.slf4j.Logger;
@@ -45,11 +38,9 @@ import org.springframework.webflow.execution.RequestContext;
  * Create a SAML Error Response
  */
 @Component
-public class SamlResponseErrorCreator {
+public class SamlResponseErrorCreator extends AbstractSamlResponseCreator {
 
     private static final Logger LOG = LoggerFactory.getLogger(SamlResponseErrorCreator.class);
-    private boolean supportDeflateEncoding;
-    private boolean useRealmForIssuer;
 
     public String createSAMLResponse(RequestContext context, boolean logout, boolean requestor,
                                      Idp idp, String requestID, String destination) throws ProcessingException {
@@ -82,49 +73,4 @@ public class SamlResponseErrorCreator {
         }
     }
 
-    protected Element createLogoutResponse(Idp idp, String statusValue,
-                                           String destination, String requestID) throws Exception {
-        Document doc = DOMUtils.newDocument();
-
-        Status status =
-            SAML2PResponseComponentBuilder.createStatus(statusValue, null);
-        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
-        LogoutResponse response =
-            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination);
-
-        Element policyElement = OpenSAMLUtil.toDom(response, doc);
-        doc.appendChild(policyElement);
-
-        return policyElement;
-    }
-
-    protected String encodeResponse(Element response) throws IOException {
-        String responseMessage = DOM2Writer.nodeToString(response);
-        LOG.debug("Created Response: {}", responseMessage);
-
-        if (supportDeflateEncoding) {
-            DeflateEncoderDecoder encoder = new DeflateEncoderDecoder();
-            byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes(StandardCharsets.UTF_8));
-
-            return Base64Utility.encode(deflatedBytes);
-        }
-
-        return Base64Utility.encode(responseMessage.getBytes());
-    }
-
-    public boolean isSupportDeflateEncoding() {
-        return supportDeflateEncoding;
-    }
-
-    public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
-        this.supportDeflateEncoding = supportDeflateEncoding;
-    }
-
-    public boolean isUseRealmForIssuer() {
-        return useRealmForIssuer;
-    }
-
-    public void setUseRealmForIssuer(boolean useRealmForIssuer) {
-        this.useRealmForIssuer = useRealmForIssuer;
-    }
 }
diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
index 6aa8985..57b4df9 100644
--- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
+++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
@@ -37,23 +37,6 @@ import java.util.UUID;
 
 import javax.servlet.ServletException;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import com.gargoylesoftware.htmlunit.CookieManager;
-import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
-import com.gargoylesoftware.htmlunit.HttpMethod;
-import com.gargoylesoftware.htmlunit.WebClient;
-import com.gargoylesoftware.htmlunit.WebRequest;
-import com.gargoylesoftware.htmlunit.html.DomElement;
-import com.gargoylesoftware.htmlunit.html.DomNodeList;
-import com.gargoylesoftware.htmlunit.html.HtmlForm;
-import com.gargoylesoftware.htmlunit.html.HtmlPage;
-import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
-import com.gargoylesoftware.htmlunit.util.NameValuePair;
-import com.gargoylesoftware.htmlunit.xml.XmlPage;
-
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.LifecycleState;
 import org.apache.catalina.connector.Connector;
@@ -96,6 +79,22 @@ 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;
+import org.w3c.dom.Node;
+
+import com.gargoylesoftware.htmlunit.CookieManager;
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.HttpMethod;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebRequest;
+import com.gargoylesoftware.htmlunit.html.DomElement;
+import com.gargoylesoftware.htmlunit.html.DomNodeList;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+import com.gargoylesoftware.htmlunit.util.NameValuePair;
+import com.gargoylesoftware.htmlunit.xml.XmlPage;
 
 /**
  * Some tests invoking directly on the IdP for SAML SSO
@@ -1737,6 +1736,8 @@ public class IdpTest {
         String success = "urn:oasis:names:tc:SAML:2.0:status:Success";
         Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue());
 
+        Assert.assertNotNull(logoutResponse.getSignature());
+
         webClient.close();
 
         // 3. now we try to access the idp without authentication but with the existing cookies
@@ -1864,6 +1865,8 @@ public class IdpTest {
         Assert.assertEquals(expectedIssuer, logoutResponse.getIssuer().getValue());
         String success = "urn:oasis:names:tc:SAML:2.0:status:Requester";
         Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue());
+
+        Assert.assertNotNull(logoutResponse.getSignature());
         webClient.close();
 
         // 3. now we try to access the idp without authentication but with the existing cookies
@@ -1996,6 +1999,7 @@ public class IdpTest {
         String success = "urn:oasis:names:tc:SAML:2.0:status:Requester";
         Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue());
 
+        Assert.assertNotNull(logoutResponse.getSignature());
         webClient.close();
 
         // 3. now we try to access the idp without authentication but with the existing cookies
@@ -2105,4 +2109,5 @@ public class IdpTest {
 
         return samlResponseObject;
     }
+
 }