Add explicit PreviousSession support
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / authn / AuthenticationEngine.java
index 5f9f77b..b289189 100644 (file)
 package edu.internet2.middleware.shibboleth.idp.authn;
 
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.security.auth.Subject;
 import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -31,12 +35,12 @@ import javax.servlet.http.HttpSession;
 
 import org.joda.time.DateTime;
 import org.opensaml.xml.util.DatatypeHelper;
-import org.opensaml.xml.util.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
+import edu.internet2.middleware.shibboleth.idp.authn.provider.PreviousSessionLoginHandler;
 import edu.internet2.middleware.shibboleth.idp.profile.IdPProfileHandlerManager;
 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
@@ -49,29 +53,37 @@ import edu.internet2.middleware.shibboleth.idp.session.impl.ServiceInformationIm
  */
 public class AuthenticationEngine extends HttpServlet {
 
+    /** Name of the IdP Cookie containing the IdP session ID. */
+    public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
+
     /** Serial version UID. */
     private static final long serialVersionUID = 8494202791991613148L;
 
     /** Class logger. */
     private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEngine.class);
 
-    /**
-     * Gets the manager used to retrieve handlers for requests.
-     * 
-     * @return manager used to retrieve handlers for requests
-     */
-    public IdPProfileHandlerManager getProfileHandlerManager() {
-        return (IdPProfileHandlerManager) getServletContext().getAttribute("handlerManager");
-    }
+    /** Profile handler manager. */
+    private IdPProfileHandlerManager handlerManager;
 
-    /**
-     * Gets the session manager to be used.
-     * 
-     * @return session manager to be used
-     */
-    @SuppressWarnings("unchecked")
-    public SessionManager<Session> getSessionManager() {
-        return (SessionManager<Session>) getServletContext().getAttribute("sessionManager");
+    /** Session manager. */
+    private SessionManager<Session> sessionManager;
+
+    /** {@inheritDoc} */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+
+        String handlerManagerId = config.getInitParameter("handlerManagerId");
+        if (DatatypeHelper.isEmpty(handlerManagerId)) {
+            handlerManagerId = "shibboleth.HandlerManager";
+        }
+        handlerManager = (IdPProfileHandlerManager) getServletContext().getAttribute(handlerManagerId);
+
+        String sessionManagerId = config.getInitParameter("sessionManagedId");
+        if (DatatypeHelper.isEmpty(sessionManagerId)) {
+            sessionManagerId = "shibboleth.SessionManager";
+        }
+
+        sessionManager = (SessionManager<Session>) getServletContext().getAttribute(sessionManagerId);
     }
 
     /**
@@ -86,8 +98,10 @@ public class AuthenticationEngine extends HttpServlet {
         LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
         if (loginContext == null) {
             LOG.error("User HttpSession did not contain a login context.  Unable to return to authentication engine");
+            forwardRequest("/idp-error.jsp", httpRequest, httpResponse);
+        } else {
+            forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
         }
-        forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
     }
 
     /**
@@ -100,6 +114,8 @@ public class AuthenticationEngine extends HttpServlet {
     public static void returnToProfileHandler(LoginContext loginContext, HttpServletRequest httpRequest,
             HttpServletResponse httpResponse) {
         LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
+        httpRequest.getSession().removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        httpRequest.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
         forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
     }
 
@@ -133,201 +149,330 @@ public class AuthenticationEngine extends HttpServlet {
             LOG.error("HTTP Response already committed");
         }
 
-        HttpSession httpSession = httpRequest.getSession();
-        LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        LoginContext loginContext = (LoginContext) httpRequest.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        if (loginContext == null) {
+            // When the login context comes from the profile handlers its attached to the request
+            // The authn engine attaches it to the session to allow the handlers to do any number of
+            // request/response pairs without maintaining or losing the login context
+            loginContext = (LoginContext) httpRequest.getSession().getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        }
+
         if (loginContext == null) {
             LOG.error("Incoming request does not have attached login context");
             throw new ServletException("Incoming request does not have attached login context");
         }
 
         if (!loginContext.getAuthenticationAttempted()) {
-            String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-            Session shibSession = getSessionManager().getSession(shibSessionId);
-
-            if (shibSession != null) {
-                AuthenticationMethodInformation authenticationMethod = getUsableExistingAuthenticationMethod(
-                        loginContext, shibSession);
-                if (authenticationMethod != null) {
-                    LOG
-                            .debug(
-                                    "An active authentication method is applicable for relying party.  "
-                                            + "Using authentication method {} as authentication method to relying party without re-authenticating user.",
-                                    authenticationMethod.getAuthenticationMethod());
-                    authenticateUserWithActiveMethod(httpRequest, httpResponse, authenticationMethod);
+            startUserAuthentication(loginContext, httpRequest, httpResponse);
+        } else {
+            completeAuthentication(loginContext, httpRequest, httpResponse);
+        }
+    }
+
+    /**
+     * 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);
+            Map<String, LoginHandler> possibleLoginHandlers = determinePossibleLoginHandlers(loginContext);
+
+            // Filter out possible candidate login handlers by forced and passive authentication requirements
+            if (loginContext.isForceAuthRequired()) {
+                filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
+            }
+
+            if (loginContext.isPassiveAuthRequired()) {
+                filterByPassiveAuthentication(loginContext, possibleLoginHandlers);
+            }
+
+            // If the user already has a session and its usage is acceptable than use it
+            // otherwise just use the first candidate login handler
+            if (idpSession != null
+                    && possibleLoginHandlers.containsKey(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD)) {
+                authenticateUserWithPreviousSession(loginContext, possibleLoginHandlers, httpRequest, httpResponse);
+            } else {
+                Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
+                authenticateUser(chosenLoginHandler.getKey(), chosenLoginHandler.getValue(), loginContext, httpRequest,
+                        httpResponse);
+            }
+        } catch (AuthenticationException e) {
+            loginContext.setAuthenticationFailure(e);
+            returnToProfileHandler(loginContext, httpRequest, httpResponse);
+        }
+
+    }
+
+    /**
+     * Determines which configured login handlers will support the requested authentication methods.
+     * 
+     * @param loginContext current login context
+     * 
+     * @return login methods that may be used to authenticate the user
+     * 
+     * @throws AuthenticationException thrown if no login handler meets the given requirements
+     */
+    protected Map<String, LoginHandler> determinePossibleLoginHandlers(LoginContext loginContext)
+            throws AuthenticationException {
+        Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(handlerManager
+                .getLoginHandlers());
+        LOG.trace("Supported login handlers: {}", supportedLoginHandlers);
+        LOG.trace("Requested authentication methods: {}", loginContext.getRequestedAuthenticationMethods());
+
+        Iterator<Entry<String, LoginHandler>> supportedLoginHandlerItr = supportedLoginHandlers.entrySet().iterator();
+        Entry<String, LoginHandler> supportedLoginHandler;
+        while (supportedLoginHandlerItr.hasNext()) {
+            supportedLoginHandler = supportedLoginHandlerItr.next();
+            if (supportedLoginHandler.getKey().equals(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD)
+                    || !loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
+                supportedLoginHandlerItr.remove();
+                continue;
+            }
+        }
+
+        if (supportedLoginHandlers.isEmpty()) {
+            LOG.error("No authentication method, requested by the service provider, is supported");
+            throw new AuthenticationException(
+                    "No authentication method, requested by the service provider, is supported");
+        }
+
+        return supportedLoginHandlers;
+    }
+
+    /**
+     * Filters out any login handler based on the requirement for forced authentication.
+     * 
+     * During forced authentication any handler that has not previously been used to authenticate the user or any
+     * handlers that have been and support force re-authentication may be used. Filter out any of the other ones.
+     * 
+     * @param idpSession user's current IdP session
+     * @param loginContext current login context
+     * @param loginHandlers login handlers to filter
+     * 
+     * @throws ForceAuthenticationException thrown if no handlers remain after filtering
+     */
+    protected void filterByForceAuthentication(Session idpSession, LoginContext loginContext,
+            Map<String, LoginHandler> loginHandlers) throws ForceAuthenticationException {
+        LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
+
+        ArrayList<AuthenticationMethodInformation> activeMethods = new ArrayList<AuthenticationMethodInformation>();
+        if (idpSession != null) {
+            activeMethods.addAll(idpSession.getAuthenticationMethods().values());
+        }
+
+        LoginHandler loginHandler;
+        for (AuthenticationMethodInformation activeMethod : activeMethods) {
+            loginHandler = loginHandlers.get(activeMethod.getAuthenticationMethod());
+            if (loginHandler != null && !loginHandler.supportsForceAuthentication()) {
+                for (String handlerSupportedMethods : loginHandler.getSupportedAuthenticationMethods()) {
+                    loginHandlers.remove(handlerSupportedMethods);
                 }
             }
+        }
 
-            LOG.debug("No active authentication method is applicable for relying party.  "
-                    + "Authenticating user with to be determined method.");
-            authenticateUserWithoutActiveMethod1(httpRequest, httpResponse);
-        } else {
-            LOG.debug("Request returned from authentication handler, completing authentication process.");
-            authenticateUserWithoutActiveMethod2(httpRequest, httpResponse);
+        if (loginHandlers.isEmpty()) {
+            LOG.error("Force authentication required but no login handlers available to support it");
+            throw new ForceAuthenticationException();
         }
+    }
 
-        return;
+    /**
+     * Filters out any login handler that doesn't support passive authentication if the login context indicates passive
+     * authentication is required.
+     * 
+     * @param loginContext current login context
+     * @param loginHandlers login handlers to filter
+     * 
+     * @throws PassiveAuthenticationException thrown if no handlers remain after filtering
+     */
+    protected void filterByPassiveAuthentication(LoginContext loginContext, Map<String, LoginHandler> loginHandlers)
+            throws PassiveAuthenticationException {
+        LOG.debug("Passive authentication is required, filtering poassible login handlers accordingly.");
+
+        LoginHandler loginHandler;
+        Iterator<Entry<String, LoginHandler>> authnMethodItr = loginHandlers.entrySet().iterator();
+        while (authnMethodItr.hasNext()) {
+            loginHandler = authnMethodItr.next().getValue();
+            if (!loginHandler.supportsPassive()) {
+                authnMethodItr.remove();
+            }
+        }
+
+        if (loginHandlers.isEmpty()) {
+            LOG.error("Passive authentication required but no login handlers available to support it");
+            throw new PassiveAuthenticationException();
+        }
     }
 
     /**
      * Completes the authentication request using an existing, active, authentication method for the current user.
      * 
+     * @param loginContext current login context
+     * @param possibleLoginHandlers login handlers that meet the peers authentication requirements
      * @param httpRequest current HTTP request
      * @param httpResponse current HTTP response
-     * @param authenticationMethod authentication method to use to complete the request
      */
-    protected void authenticateUserWithActiveMethod(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
-            AuthenticationMethodInformation authenticationMethod) {
-        HttpSession httpSession = httpRequest.getSession();
+    protected void authenticateUserWithPreviousSession(LoginContext loginContext,
+            Map<String, LoginHandler> possibleLoginHandlers, HttpServletRequest httpRequest,
+            HttpServletResponse httpResponse) {
+        LOG.debug("Authenticating user by way of existing session.");
 
-        String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-        Session shibSession = getSessionManager().getSession(shibSessionId);
+        Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+        PreviousSessionLoginHandler loginHandler = (PreviousSessionLoginHandler) handlerManager.getLoginHandlers().get(
+                PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD);
 
-        LOG.debug("Populating login context with existing session and authentication method information.");
-        LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-        loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
-        loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
-        loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
-        loginContext.setPrincipalAuthenticated(true);
-        loginContext.setPrincipalName(shibSession.getPrincipalName());
+        AuthenticationMethodInformation authenticationMethod = null;
+        for (String possibleAuthnMethod : possibleLoginHandlers.keySet()) {
+            authenticationMethod = idpSession.getAuthenticationMethods().get(possibleAuthnMethod);
+            if (authenticationMethod != null) {
+                break;
+            }
+        }
 
-        ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
-                authenticationMethod);
-        shibSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
+        if (loginHandler.reportPreviousSessionAuthnMethod()) {
+            loginContext.setAuthenticationDuration(loginHandler.getAuthenticationDuration());
+            loginContext.setAuthenticationInstant(new DateTime());
+            loginContext.setAuthenticationMethod(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD);
+        } else {
+            loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
+            loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
+            loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
+        }
+        loginContext.setPrincipalName(idpSession.getPrincipalName());
 
-        returnToProfileHandler(loginContext, httpRequest, httpResponse);
+        httpRequest.getSession().setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
+        loginHandler.login(httpRequest, httpResponse);
     }
 
     /**
-     * Performs the first part of user authentication. An authentication handler is determined, the login context is
-     * populated with some initial information, and control is forward to the selected handler so that it may
-     * authenticate the user.
+     * Authenticates the user with the given authentication method provided by the given login handler.
      * 
+     * @param authnMethod the authentication method that will be used to authenticate the user
+     * @param loginHandler login handler that will authenticate user
+     * @param loginContext current login context
      * @param httpRequest current HTTP request
      * @param httpResponse current HTTP response
      */
-    protected void authenticateUserWithoutActiveMethod1(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-        HttpSession httpSession = httpRequest.getSession();
-        LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-        LOG.debug("Selecting appropriate authentication method for request.");
-        Pair<String, LoginHandler> handler = getProfileHandlerManager().getAuthenticationHandler(loginContext);
-
-        if (handler == null) {
-            loginContext.setPrincipalAuthenticated(false);
-            loginContext.setAuthenticationFailureMessage("No AuthenticationHandler satisfys the request from: "
-                    + loginContext.getRelyingPartyId());
-            LOG.error("No AuthenticationHandler satisfies the request from relying party: "
-                    + loginContext.getRelyingPartyId());
-            returnToProfileHandler(loginContext, httpRequest, httpResponse);
-            return;
-        }
+    protected void authenticateUser(String authnMethod, LoginHandler loginHandler, LoginContext loginContext,
+            HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+        LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
 
-        LOG.debug("Authentication method {} will be used to authenticate user.", handler.getFirst());
         loginContext.setAuthenticationAttempted();
-        loginContext.setAuthenticationDuration(handler.getSecond().getAuthenticationDuration());
-        loginContext.setAuthenticationMethod(handler.getFirst());
+        loginContext.setAuthenticationInstant(new DateTime());
+        loginContext.setAuthenticationDuration(loginHandler.getAuthenticationDuration());
+        loginContext.setAuthenticationMethod(authnMethod);
         loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
-
-        LOG.debug("Transferring control to authentication handler of type: {}", handler.getSecond().getClass()
-                .getName());
-        handler.getSecond().login(httpRequest, httpResponse);
+        httpRequest.getSession().setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
+        loginHandler.login(httpRequest, httpResponse);
     }
 
     /**
-     * Performs the second part of user authentication. The principal name set by the authentication handler is
-     * retrieved and pushed in to the login context, a Shibboleth session is created if needed, information indicating
-     * that the user has logged into the service is recorded and finally control is returned back to the profile
-     * handler.
+     * Completes the authentication process.
      * 
+     * The principal name set by the authentication handler is retrieved and pushed in to the login context, a
+     * Shibboleth session is created if needed, information indicating that the user has logged into the service is
+     * recorded and finally control is returned back to the profile handler.
+     * 
+     * @param loginContext current login context
      * @param httpRequest current HTTP request
      * @param httpResponse current HTTP response
      */
-    protected void authenticateUserWithoutActiveMethod2(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-        HttpSession httpSession = httpRequest.getSession();
+    protected void completeAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
+            HttpServletResponse httpResponse) {
 
-        String principalName = (String) httpRequest.getAttribute(LoginHandler.PRINCIPAL_NAME_KEY);
-        LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-        if (DatatypeHelper.isEmpty(principalName)) {
-            loginContext.setPrincipalAuthenticated(false);
-            loginContext.setAuthenticationFailureMessage("No principal name returned from authentication handler.");
-            LOG.error("No principal name returned from authentication method: "
-                    + loginContext.getAuthenticationMethod());
-            returnToProfileHandler(loginContext, httpRequest, httpResponse);
-            return;
+        // We check if the principal name was already set in the login context
+        // if not attempt to pull it from where login handlers are supposed to provide it
+        String principalName = loginContext.getPrincipalName();
+        if (principalName == null) {
+            DatatypeHelper.safeTrimOrNullString((String) httpRequest.getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
+            if (principalName != null) {
+                loginContext.setPrincipalName(principalName);
+            }else{
+                loginContext.setPrincipalAuthenticated(false);
+                loginContext.setAuthenticationFailure(new AuthenticationException(
+                        "No principal name returned from authentication handler."));
+                LOG.error("No principal name returned from authentication method: "
+                        + loginContext.getAuthenticationMethod());
+                returnToProfileHandler(loginContext, httpRequest, httpResponse);
+                return;
+            }
+        }
+        loginContext.setPrincipalAuthenticated(true);
+        
+        // We allow a login handler to override the authentication method in the event that it supports multiple methods
+        String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+                .getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY));
+        if (actualAuthnMethod != null) {
+            loginContext.setAuthenticationMethod(actualAuthnMethod);
         }
-        loginContext.setPrincipalName(principalName);
-        loginContext.setAuthenticationInstant(new DateTime());
-
-        String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-        Session shibSession = getSessionManager().getSession(shibSessionId);
-
-        if (shibSession == null) {
-            LOG.debug("Creating shibboleth session for principal {}", principalName);
 
-            InetAddress addr;
-            try {
-                addr = InetAddress.getByName(httpRequest.getRemoteAddr());
-            } catch (UnknownHostException ex) {
-                addr = null;
-            }
+        LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), loginContext
+                .getAuthenticationMethod());
+        updateUserSession(loginContext, httpRequest, httpResponse);
+        returnToProfileHandler(loginContext, httpRequest, httpResponse);
+    }
 
-            shibSession = (Session) getSessionManager().createSession(addr, loginContext.getPrincipalName());
-            loginContext.setSessionID(shibSession.getSessionID());
-            httpSession.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, shibSession.getSessionID());
+    /**
+     * 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 httpRequest current HTTP request
+     * @param httpResponse current HTTP response
+     */
+    protected void updateUserSession(LoginContext loginContext, HttpServletRequest httpRequest,
+            HttpServletResponse httpResponse) {
+        Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+        if (idpSession == null) {
+            LOG.debug("Creating shibboleth session for principal {}", loginContext.getPrincipalName());
+            idpSession = (Session) sessionManager.createSession(loginContext.getPrincipalName());
+            loginContext.setSessionID(idpSession.getSessionID());
+            addSessionCookie(httpRequest, httpResponse, idpSession);
         }
 
         LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
-                principalName);
+                loginContext.getPrincipalName());
         Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
-        AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, loginContext
-                .getAuthenticationMethod(), new DateTime(), loginContext.getAuthenticationDuration());
+        String authnMethod = (String) httpRequest.getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY);
+        if (DatatypeHelper.isEmpty(authnMethod)) {
+            authnMethod = loginContext.getAuthenticationMethod();
+        }
+
+        AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, authnMethod,
+                loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
 
-        shibSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
+        idpSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
 
         ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
                 authnMethodInfo);
-        shibSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
-
-        shibSession.setLastActivityInstant(new DateTime());
-
-        returnToProfileHandler(loginContext, httpRequest, httpResponse);
+        idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
     }
 
     /**
-     * Gets the authentication method, currently active for the user, that also meets the requirements expressed by the
-     * login context. If a method is returned the user does not need to authenticate again, if null is returned then the
-     * user must be authenticated.
-     * 
-     * @param loginContext user login context
-     * @param shibSession user's shibboleth session
+     * Adds an IdP session cookie to the outbound response.
      * 
-     * @return active authentication method that meets authentication requirements or null
+     * @param httpRequest current request
+     * @param httpResponse current response
+     * @param userSession user's session
      */
-    protected AuthenticationMethodInformation getUsableExistingAuthenticationMethod(LoginContext loginContext,
-            Session shibSession) {
-        if (loginContext.getForceAuth() || shibSession == null) {
-            return null;
-        }
-
-        List<String> preferredAuthnMethods = loginContext.getRequestedAuthenticationMethods();
+    protected void addSessionCookie(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
+            Session userSession) {
+        httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);
 
-        if (preferredAuthnMethods == null || preferredAuthnMethods.size() == 0) {
-            for (AuthenticationMethodInformation authnMethod : shibSession.getAuthenticationMethods().values()) {
-                if (!authnMethod.isExpired()) {
-                    return authnMethod;
-                }
-            }
-        } else {
-            for (String preferredAuthnMethod : preferredAuthnMethods) {
-                if (shibSession.getAuthenticationMethods().containsKey(preferredAuthnMethod)) {
-                    AuthenticationMethodInformation authnMethodInfo = shibSession.getAuthenticationMethods().get(
-                            preferredAuthnMethod);
-                    if (!authnMethodInfo.isExpired()) {
-                        return authnMethodInfo;
-                    }
-                }
-            }
-        }
+        LOG.debug("Adding IdP session cookie to HTTP response");
+        Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
+        sessionCookie.setPath(httpRequest.getContextPath());
+        sessionCookie.setSecure(false);
+        sessionCookie.setMaxAge(-1);
 
-        return null;
+        httpResponse.addCookie(sessionCookie);
     }
 }
\ No newline at end of file