/**
   * Updates the user's Shibboleth session with authentication information. If no session exists a
   * new one will be created.
   *
   * @param loginContext current login context
   * @param authenticationSubject subject created from the authentication method
   * @param authenticationMethod the method used to authenticate the subject
   * @param authenticationInstant the time of authentication
   * @param httpRequest current HTTP request
   * @param httpResponse current HTTP response
   */
  protected void updateUserSession(
      LoginContext loginContext,
      Subject authenticationSubject,
      String authenticationMethod,
      DateTime authenticationInstant,
      HttpServletRequest httpRequest,
      HttpServletResponse httpResponse) {
    Principal authenticationPrincipal = authenticationSubject.getPrincipals().iterator().next();
    LOG.debug("Updating session information for principal {}", authenticationPrincipal.getName());

    Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
    if (idpSession == null) {
      LOG.debug("Creating shibboleth session for principal {}", authenticationPrincipal.getName());
      idpSession = (Session) sessionManager.createSession();
      loginContext.setSessionID(idpSession.getSessionID());
      addSessionCookie(httpRequest, httpResponse, idpSession);
    }

    // Merge the information in the current session subject with the information from the
    // login handler subject
    idpSession.setSubject(mergeSubjects(idpSession.getSubject(), authenticationSubject));

    // Check if an existing authentication method with no updated timestamp was used (i.e. SSO
    // occurred);
    // if not record the new information
    AuthenticationMethodInformation authnMethodInfo =
        idpSession.getAuthenticationMethods().get(authenticationMethod);
    if (authnMethodInfo == null || authenticationInstant != null) {
      LOG.debug(
          "Recording authentication and service information in Shibboleth session for principal: {}",
          authenticationPrincipal.getName());
      LoginHandler loginHandler =
          handlerManager.getLoginHandlers().get(loginContext.getAttemptedAuthnMethod());
      DateTime authnInstant = authenticationInstant;
      if (authnInstant == null) {
        authnInstant = new DateTime();
      }
      authnMethodInfo =
          new AuthenticationMethodInformationImpl(
              idpSession.getSubject(),
              authenticationPrincipal,
              authenticationMethod,
              authnInstant,
              loginHandler.getAuthenticationDuration());
    }

    loginContext.setAuthenticationMethodInformation(authnMethodInfo);
    idpSession
        .getAuthenticationMethods()
        .put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
    sessionManager.indexSession(idpSession, idpSession.getPrincipalName());

    ServiceInformation serviceInfo =
        new ServiceInformationImpl(
            loginContext.getRelyingPartyId(), new DateTime(), authnMethodInfo);
    idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
  }
  /**
   * Adds an IdP session cookie to the outbound response.
   *
   * @param httpRequest current request
   * @param httpResponse current response
   * @param userSession user's session
   */
  protected void addSessionCookie(
      HttpServletRequest httpRequest, HttpServletResponse httpResponse, Session userSession) {
    httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);

    byte[] remoteAddress = httpRequest.getRemoteAddr().getBytes();
    byte[] sessionId = userSession.getSessionID().getBytes();

    String signature = null;
    try {
      MessageDigest digester = MessageDigest.getInstance("SHA");
      digester.update(userSession.getSessionSecret());
      digester.update(remoteAddress);
      digester.update(sessionId);
      signature = Base64.encodeBytes(digester.digest());
    } catch (GeneralSecurityException e) {
      LOG.error("Unable to compute signature over session cookie material", e);
    }

    LOG.debug("Adding IdP session cookie to HTTP response");
    StringBuilder cookieValue = new StringBuilder();
    cookieValue.append(Base64.encodeBytes(remoteAddress, Base64.DONT_BREAK_LINES)).append("|");
    cookieValue.append(Base64.encodeBytes(sessionId, Base64.DONT_BREAK_LINES)).append("|");
    cookieValue.append(signature);

    String cookieDomain = HttpServletHelper.getCookieDomain(context);

    Cookie sessionCookie =
        new Cookie(IDP_SESSION_COOKIE_NAME, HTTPTransportUtils.urlEncode(cookieValue.toString()));
    sessionCookie.setVersion(1);
    if (cookieDomain != null) {
      sessionCookie.setDomain(cookieDomain);
    }
    sessionCookie.setPath(
        "".equals(httpRequest.getContextPath()) ? "/" : httpRequest.getContextPath());
    sessionCookie.setSecure(httpRequest.isSecure());
    httpResponse.addCookie(sessionCookie);
  }