/**
   * 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);
  }
  /**
   * Begins the authentication process. Determines if forced re-authentication is required or if an
   * existing, active, authentication method is sufficient. Also determines, when authentication is
   * required, which handler to use depending on whether passive authentication is required.
   *
   * @param loginContext current login context
   * @param httpRequest current HTTP request
   * @param httpResponse current HTTP response
   */
  protected void startUserAuthentication(
      LoginContext loginContext, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
    LOG.debug("Beginning user authentication process.");
    try {
      Session idpSession =
          (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
      if (idpSession != null) {
        LOG.debug("Existing IdP session available for principal {}", idpSession.getPrincipalName());
      }

      Map<String, LoginHandler> possibleLoginHandlers =
          determinePossibleLoginHandlers(idpSession, loginContext);

      // Filter out possible candidate login handlers by forced and passive authentication
      // requirements
      if (loginContext.isForceAuthRequired()) {
        filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
      }

      if (loginContext.isPassiveAuthRequired()) {
        filterByPassiveAuthentication(idpSession, loginContext, possibleLoginHandlers);
      }

      LoginHandler loginHandler =
          selectLoginHandler(possibleLoginHandlers, loginContext, idpSession);
      loginContext.setAuthenticationAttempted();
      loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));

      // Send the request to the login handler
      HttpServletHelper.bindLoginContext(
          loginContext, storageService, getServletContext(), httpRequest, httpResponse);
      loginHandler.login(httpRequest, httpResponse);
    } catch (AuthenticationException e) {
      loginContext.setAuthenticationFailure(e);
      returnToProfileHandler(httpRequest, httpResponse);
    }
  }