public void processLogoutResponse(SAMLMessageContext context)
      throws SAMLException, org.opensaml.xml.security.SecurityException, ValidationException {

    SAMLObject message = context.getInboundSAMLMessage();

    // Verify type
    if (!(message instanceof LogoutResponse)) {
      log.debug("Received response is not of a Response object type");
      throw new SAMLException("Error validating SAML response");
    }
    LogoutResponse response = (LogoutResponse) message;

    // Make sure request was authenticated if required, authentication is done as part of the
    // binding processing
    if (!context.isInboundSAMLMessageAuthenticated()
        && context.getLocalExtendedMetadata().isRequireLogoutResponseSigned()) {
      log.debug(
          "Logout Response object is required to be signed by the entity policy: "
              + context.getInboundSAMLMessageId());
      throw new SAMLException("Logout Response object is required to be signed");
    }

    // Verify issue time
    DateTime time = response.getIssueInstant();
    if (!isDateTimeSkewValid(getResponseSkew(), time)) {
      log.debug("Response issue time is either too old or with date in the future");
      throw new SAMLException("Error validating SAML response");
    }

    // Verify response to field if present, set request if correct
    // The inResponseTo field is optional, SAML 2.0 Core, 1542
    SAMLMessageStorage messageStorage = context.getMessageStorage();
    if (messageStorage != null && response.getInResponseTo() != null) {
      XMLObject xmlObject = messageStorage.retrieveMessage(response.getInResponseTo());
      if (xmlObject == null) {
        log.debug(
            "InResponseToField doesn't correspond to sent message", response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      } else if (xmlObject instanceof LogoutRequest) {
        // Expected
      } else {
        log.debug(
            "Sent request was of different type then received response",
            response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify destination
    if (response.getDestination() != null) {
      SPSSODescriptor localDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata();

      // Check if destination is correct on this SP
      List<SingleLogoutService> services = localDescriptor.getSingleLogoutServices();
      boolean found = false;
      for (SingleLogoutService service : services) {
        if (response.getDestination().equals(service.getLocation())
            && context.getInboundSAMLBinding().equals(service.getBinding())) {
          found = true;
          break;
        }
      }
      if (!found) {
        log.debug(
            "Destination of the response was not the expected value", response.getDestination());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify issuer
    if (response.getIssuer() != null) {
      Issuer issuer = response.getIssuer();
      verifyIssuer(issuer, context);
    }

    // Verify status
    String statusCode = response.getStatus().getStatusCode().getValue();
    if (StatusCode.SUCCESS_URI.equals(statusCode)) {
      log.trace("Single Logout was successful");
    } else if (StatusCode.PARTIAL_LOGOUT_URI.equals(statusCode)) {
      log.trace("Single Logout was partially successful");
    } else {
      String[] logMessage = new String[2];
      logMessage[0] = response.getStatus().getStatusCode().getValue();
      StatusMessage message1 = response.getStatus().getStatusMessage();
      if (message1 != null) {
        logMessage[1] = message1.getMessage();
      }
      log.warn("Received LogoutResponse has invalid status code", logMessage);
    }
  }
  public boolean processLogoutRequest(SAMLMessageContext context, SAMLCredential credential)
      throws SAMLException, MetadataProviderException, MessageEncodingException {

    SAMLObject message = context.getInboundSAMLMessage();

    // Verify type
    if (message == null || !(message instanceof LogoutRequest)) {
      log.warn("Received request is not of a LogoutRequest object type");
      throw new SAMLException("Error validating SAML request");
    }

    LogoutRequest logoutRequest = (LogoutRequest) message;

    // Make sure request was authenticated if required, authentication is done as part of the
    // binding processing
    if (!context.isInboundSAMLMessageAuthenticated()
        && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) {
      log.warn(
          "Logout Request object is required to be signed by the entity policy: "
              + context.getInboundSAMLMessageId());
      Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Message signature is required");
      sendLogoutResponse(status, context);
      return false;
    }

    try {
      // Verify destination
      verifyEndpoint(context.getLocalEntityEndpoint(), logoutRequest.getDestination());
    } catch (SAMLException e) {
      log.warn(
          "Destination of the request {} does not match any singleLogout endpoint",
          logoutRequest.getDestination());
      Status status =
          getStatus(StatusCode.REQUEST_DENIED_URI, "Destination URL of the request is invalid");
      sendLogoutResponse(status, context);
      return false;
    }

    // Verify issuer
    if (logoutRequest.getIssuer() != null) {
      try {
        Issuer issuer = logoutRequest.getIssuer();
        verifyIssuer(issuer, context);
      } catch (SAMLException e) {
        log.warn(
            "Response issue time is either too old or with date in the future, id {}",
            context.getInboundSAMLMessageId());
        Status status =
            getStatus(StatusCode.REQUEST_DENIED_URI, "Issuer of the message is unknown");
        sendLogoutResponse(status, context);
        return false;
      }
    }

    // Verify issue time
    DateTime time = logoutRequest.getIssueInstant();
    if (!isDateTimeSkewValid(getResponseSkew(), time)) {
      log.warn(
          "Response issue time is either too old or with date in the future, id {}.",
          context.getInboundSAMLMessageId());
      Status status =
          getStatus(StatusCode.REQUESTER_URI, "Message has been issued too long time ago");
      sendLogoutResponse(status, context);
      return false;
    }

    // Check whether any user is logged in
    if (credential == null) {
      Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "No user is logged in");
      sendLogoutResponse(status, context);
      return false;
    }

    // Find index for which the logout is requested
    boolean indexFound = false;
    if (logoutRequest.getSessionIndexes() != null && logoutRequest.getSessionIndexes().size() > 0) {
      for (AuthnStatement statement :
          credential.getAuthenticationAssertion().getAuthnStatements()) {
        String statementIndex = statement.getSessionIndex();
        if (statementIndex != null) {
          for (SessionIndex index : logoutRequest.getSessionIndexes()) {
            if (statementIndex.equals(index.getSessionIndex())) {
              indexFound = true;
            }
          }
        }
      }
    } else {
      indexFound = true;
    }

    // Fail if sessionIndex is not found in any assertion
    if (!indexFound) {

      // Check logout request still valid and store request
      if (logoutRequest.getNotOnOrAfter() != null) {
        // TODO store request for assertions possibly arriving later
      }

      Status status =
          getStatus(StatusCode.REQUESTER_URI, "The requested SessionIndex was not found");
      sendLogoutResponse(status, context);
      return false;
    }

    try {
      // Fail if NameId doesn't correspond to the currently logged user
      NameID nameID = getNameID(context, logoutRequest);
      if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) {
        Status status =
            getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid");
        sendLogoutResponse(status, context);
        return false;
      }
    } catch (DecryptionException e) {
      Status status = getStatus(StatusCode.RESPONDER_URI, "The NameID can't be decrypted");
      sendLogoutResponse(status, context);
      return false;
    }

    // Message is valid, let's logout
    Status status = getStatus(StatusCode.SUCCESS_URI, null);
    sendLogoutResponse(status, context);

    return true;
  }