public static void setTokenCookie(
      KeycloakDeployment deployment,
      HttpFacade facade,
      RefreshableKeycloakSecurityContext session) {
    log.debugf("Set new %s cookie now", AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
    String accessToken = session.getTokenString();
    String idToken = session.getIdTokenString();
    String refreshToken = session.getRefreshToken();
    String cookie =
        new StringBuilder(accessToken)
            .append(DELIM)
            .append(idToken)
            .append(DELIM)
            .append(refreshToken)
            .toString();

    String cookiePath = getContextPath(facade);
    facade
        .getResponse()
        .setCookie(
            AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE,
            cookie,
            cookiePath,
            null,
            -1,
            deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()),
            true);
  }
 protected boolean verifySSL() {
   if (!facade.getRequest().isSecure()
       && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
     log.warn("SSL is required to authenticate");
     return true;
   }
   return false;
 }
  public static KeycloakPrincipal<RefreshableKeycloakSecurityContext> getPrincipalFromCookie(
      KeycloakDeployment deployment, HttpFacade facade, AdapterTokenStore tokenStore) {
    OIDCHttpFacade.Cookie cookie =
        facade.getRequest().getCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
    if (cookie == null) {
      log.debug("Not found adapter state cookie in current request");
      return null;
    }

    String cookieVal = cookie.getValue();

    String[] tokens = cookieVal.split(DELIM);
    if (tokens.length != 3) {
      log.warnf(
          "Invalid format of %s cookie. Count of tokens: %s, expected 3",
          AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, tokens.length);
      return null;
    }

    String accessTokenString = tokens[0];
    String idTokenString = tokens[1];
    String refreshTokenString = tokens[2];

    try {
      // Skip check if token is active now. It's supposed to be done later by the caller
      AccessToken accessToken =
          AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
      IDToken idToken;
      if (idTokenString != null && idTokenString.length() > 0) {
        try {
          JWSInput input = new JWSInput(idTokenString);
          idToken = input.readJsonContent(IDToken.class);
        } catch (JWSInputException e) {
          throw new VerificationException(e);
        }
      } else {
        idToken = null;
      }

      log.debug("Token Verification succeeded!");
      RefreshableKeycloakSecurityContext secContext =
          new RefreshableKeycloakSecurityContext(
              deployment,
              tokenStore,
              accessTokenString,
              accessToken,
              idTokenString,
              idToken,
              refreshTokenString);
      return new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(
          AdapterUtils.getPrincipalName(deployment, accessToken), secContext);
    } catch (VerificationException ve) {
      log.warn("Failed verify token", ve);
      return null;
    }
  }
  protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
    SAMLDocumentHolder holder = null;
    boolean postBinding = false;
    String requestUri = facade.getRequest().getURI();
    if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
      // strip out query params
      int index = requestUri.indexOf('?');
      if (index > -1) {
        requestUri = requestUri.substring(0, index);
      }
      holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
    } else {
      postBinding = true;
      holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
    }
    RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
    if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
      log.error(
          "expected destination '"
              + requestUri
              + "' got '"
              + requestAbstractType.getDestination()
              + "'");
      return AuthOutcome.FAILED;
    }

    if (requestAbstractType instanceof LogoutRequestType) {
      if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
        try {
          validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
        } catch (VerificationException e) {
          log.error("Failed to verify saml request signature", e);
          return AuthOutcome.FAILED;
        }
      }
      LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
      return logoutRequest(logout, relayState);

    } else {
      log.error("unknown SAML request type");
      return AuthOutcome.FAILED;
    }
  }
  public void verifyRedirectBindingSignature(PublicKey publicKey, String paramKey)
      throws VerificationException {
    String request = facade.getRequest().getQueryParamValue(paramKey);
    String algorithm =
        facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
    String signature =
        facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
    String decodedAlgorithm =
        facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);

    if (request == null) {
      throw new VerificationException("SAML Request was null");
    }
    if (algorithm == null) throw new VerificationException("SigAlg was null");
    if (signature == null) throw new VerificationException("Signature was null");

    // Shibboleth doesn't sign the document for redirect binding.
    // todo maybe a flag?

    String relayState = facade.getRequest().getQueryParamValue(GeneralConstants.RELAY_STATE);
    KeycloakUriBuilder builder = KeycloakUriBuilder.fromPath("/").queryParam(paramKey, request);
    if (relayState != null) {
      builder.queryParam(GeneralConstants.RELAY_STATE, relayState);
    }
    builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
    String rawQuery = builder.build().getRawQuery();

    try {
      // byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
      byte[] decodedSignature = Base64.decode(signature);

      SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
      Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
      validator.initVerify(publicKey);
      validator.update(rawQuery.getBytes("UTF-8"));
      if (!validator.verify(decodedSignature)) {
        throw new VerificationException("Invalid query param signature");
      }
    } catch (Exception e) {
      throw new VerificationException(e);
    }
  }
  @Override
  public void saveRequest() {
    HttpSession session = request.getSession(true);
    session.setAttribute(REDIRECT_URI, facade.getRequest().getURI());
    session.setAttribute(SAVED_METHOD, request.getMethod());
    MultivaluedHashMap<String, String> headers = new MultivaluedHashMap<>();
    Enumeration<String> names = request.getHeaderNames();
    while (names.hasMoreElements()) {
      String name = names.nextElement();
      Enumeration<String> values = request.getHeaders(name);
      while (values.hasMoreElements()) {
        headers.add(name.toLowerCase(), values.nextElement());
      }
    }
    session.setAttribute(SAVED_HEADERS, headers);
    if (request.getMethod().equalsIgnoreCase("GET")) {
      return;
    }
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    byte[] buffer = new byte[4096];
    int bytesRead;
    int totalRead = 0;
    try {
      InputStream is = request.getInputStream();

      while ((bytesRead = is.read(buffer)) >= 0) {
        os.write(buffer);
        totalRead += bytesRead;
        if (totalRead > maxBuffer) {
          throw new RuntimeException("max buffer reached on a saved request");
        }
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    byte[] body = os.toByteArray();
    // Only save the request body if there is something to save
    if (body.length > 0) {
      session.setAttribute(SAVED_BODY, body);
    }
  }
 private static String getContextPath(HttpFacade facade) {
   String uri = facade.getRequest().getURI();
   String path = KeycloakUriBuilder.fromUri(uri).getPath();
   int index = path.indexOf("/", 1);
   return index == -1 ? path : path.substring(0, index);
 }
 public static void removeCookie(HttpFacade facade) {
   String cookiePath = getContextPath(facade);
   facade.getResponse().resetCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookiePath);
 }
  protected AuthOutcome handleLoginResponse(
      ResponseType responseType, OnSessionCreated onCreateSession) {

    AssertionType assertion = null;
    try {
      assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
      if (AssertionUtil.hasExpired(assertion)) {
        return initiateLogin();
      }
    } catch (Exception e) {
      log.error("Error extracting SAML assertion: " + e.getMessage());
      challenge =
          new AuthChallenge() {
            @Override
            public boolean challenge(HttpFacade exchange) {
              SamlAuthenticationError error =
                  new SamlAuthenticationError(SamlAuthenticationError.Reason.EXTRACTION_FAILURE);
              exchange.getRequest().setError(error);
              exchange.getResponse().sendError(403);
              return true;
            }

            @Override
            public int getResponseCode() {
              return 403;
            }
          };
    }

    SubjectType subject = assertion.getSubject();
    SubjectType.STSubType subType = subject.getSubType();
    NameIDType subjectNameID = (NameIDType) subType.getBaseID();
    String principalName = subjectNameID.getValue();

    final Set<String> roles = new HashSet<>();
    MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
    MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();

    Set<StatementAbstractType> statements = assertion.getStatements();
    for (StatementAbstractType statement : statements) {
      if (statement instanceof AttributeStatementType) {
        AttributeStatementType attributeStatement = (AttributeStatementType) statement;
        List<AttributeStatementType.ASTChoiceType> attList = attributeStatement.getAttributes();
        for (AttributeStatementType.ASTChoiceType obj : attList) {
          AttributeType attr = obj.getAttribute();
          if (isRole(attr)) {
            List<Object> attributeValues = attr.getAttributeValue();
            if (attributeValues != null) {
              for (Object attrValue : attributeValues) {
                String role = getAttributeValue(attrValue);
                log.debugv("Add role: {0}", role);
                roles.add(role);
              }
            }
          } else {
            List<Object> attributeValues = attr.getAttributeValue();
            if (attributeValues != null) {
              for (Object attrValue : attributeValues) {
                String value = getAttributeValue(attrValue);
                if (attr.getName() != null) {
                  attributes.add(attr.getName(), value);
                }
                if (attr.getFriendlyName() != null) {
                  friendlyAttributes.add(attr.getFriendlyName(), value);
                }
              }
            }
          }
        }
      }
    }
    if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
      if (deployment.getPrincipalAttributeName() != null) {
        String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
        if (attribute != null) principalName = attribute;
        else {
          attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
          if (attribute != null) principalName = attribute;
        }
      }
    }

    AuthnStatementType authn = null;
    for (Object statement : assertion.getStatements()) {
      if (statement instanceof AuthnStatementType) {
        authn = (AuthnStatementType) statement;
        break;
      }
    }

    URI nameFormat = subjectNameID.getFormat();
    String nameFormatString =
        nameFormat == null
            ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get()
            : nameFormat.toString();
    final SamlPrincipal principal =
        new SamlPrincipal(
            assertion,
            principalName,
            principalName,
            nameFormatString,
            attributes,
            friendlyAttributes);
    String index = authn == null ? null : authn.getSessionIndex();
    final String sessionIndex = index;
    SamlSession account = new SamlSession(principal, roles, sessionIndex);
    sessionStore.saveAccount(account);
    onCreateSession.onSessionCreated(account);

    // redirect to original request, it will be restored
    String redirectUri = sessionStore.getRedirectUri();
    if (redirectUri != null) {
      facade.getResponse().setHeader("Location", redirectUri);
      facade.getResponse().setStatus(302);
      facade.getResponse().end();
    } else {
      log.debug("IDP initiated invocation");
    }
    log.debug("AUTHENTICATED authn");

    return AuthOutcome.AUTHENTICATED;
  }
  protected AuthOutcome handleSamlResponse(
      String samlResponse, String relayState, OnSessionCreated onCreateSession) {
    SAMLDocumentHolder holder = null;
    boolean postBinding = false;
    String requestUri = facade.getRequest().getURI();
    if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
      int index = requestUri.indexOf('?');
      if (index > -1) {
        requestUri = requestUri.substring(0, index);
      }
      holder = extractRedirectBindingResponse(samlResponse);
    } else {
      postBinding = true;
      holder = extractPostBindingResponse(samlResponse);
    }
    final StatusResponseType statusResponse = (StatusResponseType) holder.getSamlObject();
    // validate destination
    if (!requestUri.equals(statusResponse.getDestination())) {
      log.error("Request URI does not match SAML request destination");
      return AuthOutcome.FAILED;
    }

    if (statusResponse instanceof ResponseType) {
      try {
        if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
          try {
            validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
          } catch (VerificationException e) {
            log.error("Failed to verify saml response signature", e);

            challenge =
                new AuthChallenge() {
                  @Override
                  public boolean challenge(HttpFacade exchange) {
                    SamlAuthenticationError error =
                        new SamlAuthenticationError(
                            SamlAuthenticationError.Reason.INVALID_SIGNATURE);
                    exchange.getRequest().setError(error);
                    exchange.getResponse().sendError(403);
                    return true;
                  }

                  @Override
                  public int getResponseCode() {
                    return 403;
                  }
                };
            return AuthOutcome.FAILED;
          }
        }
        return handleLoginResponse((ResponseType) statusResponse, onCreateSession);
      } finally {
        sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
      }

    } else {
      if (sessionStore.isLoggingOut()) {
        try {
          if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
            try {
              validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
            } catch (VerificationException e) {
              log.error("Failed to verify saml response signature", e);
              return AuthOutcome.FAILED;
            }
          }
          return handleLogoutResponse(holder, statusResponse, relayState);
        } finally {
          sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
        }

      } else if (sessionStore.isLoggingIn()) {

        try {
          // KEYCLOAK-2107 - handle user not authenticated due passive mode. Return special outcome
          // so different authentication mechanisms can behave accordingly.
          StatusType status = statusResponse.getStatus();
          if (checkStatusCodeValue(
                  status.getStatusCode(), JBossSAMLURIConstants.STATUS_RESPONDER.get())
              && checkStatusCodeValue(
                  status.getStatusCode().getStatusCode(),
                  JBossSAMLURIConstants.STATUS_NO_PASSIVE.get())) {
            log.debug(
                "Not authenticated due passive mode Status found in SAML response: "
                    + status.toString());
            return AuthOutcome.NOT_AUTHENTICATED;
          }

          challenge =
              new AuthChallenge() {
                @Override
                public boolean challenge(HttpFacade exchange) {
                  SamlAuthenticationError error =
                      new SamlAuthenticationError(
                          SamlAuthenticationError.Reason.ERROR_STATUS, statusResponse);
                  exchange.getRequest().setError(error);
                  exchange.getResponse().sendError(403);
                  return true;
                }

                @Override
                public int getResponseCode() {
                  return 403;
                }
              };
          return AuthOutcome.FAILED;
        } finally {
          sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
        }
      }
      return AuthOutcome.NOT_ATTEMPTED;
    }
  }