https://bz.apache.org/bugzilla/show_bug.cgi?id=57937

            Bug ID: 57937
           Summary: Request for a form based two factor authentication
                    support in Tomcat
           Product: Tomcat 7
           Version: 7.0.55
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: Catalina
          Assignee: dev@tomcat.apache.org
          Reporter: bella...@denic.de

Created attachment 32744
  --> https://bz.apache.org/bugzilla/attachment.cgi?id=32744&action=edit
Result of a diff -u operated on the original code and the pseudo-code
containing a 2fa workaround

Hi there,

for an application to be deployed on a TomEE we need the support for two 
factor authentication.
Are there planned development efforts going into that direction?


As temporary workaround we patched the class FormAuthenticator, so that a
second factor (a 2fa token) is additionlly evaluated. This patch is in the
style of AuthenticRoast, an solution available for Tomcat 1.6.

Here the code patch in pseudo code:



package org.apache.catalina.authenticator;

[original code omitted with imports]

/**
 * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
Authentication, as
 * described in the Servlet API Specification, Version 2.2.
 */
public class FormAuthenticator extends AuthenticatorBase {

  [original code omitted]


  // --------------------------------------------------------- Public Methods

  /**
   * Authenticate the user making this request, based on the specified login
configuration. Return
   * <code>true</code> if any specified constraint has been satisfied, or 
<code>false</code> if we
   * have created a response challenge already.
   *
   * @param request Request we are processing
   * @param response Response we are creating
   * @param config Login configuration describing how authentication should be
performed
   *
   * @exception IOException if an input/output error occurs
   */
  @Override
  public boolean authenticate(Request request, HttpServletResponse response,
LoginConfig config) throws IOException {

    // References to objects we will need later 
    Session session = null;
    // Have we already authenticated someone?
    Principal principal = request.getUserPrincipal();
    String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);

    [original code omitted]

    // Yes -- Acknowledge the request, validate the specified credentials
    // and redirect to the error page if they are not correct
    request.getResponse().sendAcknowledgement();
    Realm realm = context.getRealm();
    if (characterEncoding != null) {
      request.setCharacterEncoding(characterEncoding);
    }

    String username = request.getParameter(Constants.FORM_USERNAME);
    String password = request.getParameter(Constants.FORM_PASSWORD);
    if (log.isDebugEnabled()) {
      log.debug("Authenticating username '" + username + "'");
    }


    // BEGIN --- Customized code for two factor authentication Part I --- BEGIN
    String password2factor = request.getParameter("j_2fa");
    // A customized Realm Class which implements a second factor authentication
    final TwoFactorRealm twoFactorRealm = new TwoFactorRealm(...);
    boolean twoFactorSuccessful = twoFactorRealm.authenticate(username,
password2factor);
    if (log.isWarnEnabled() && !twoFactorSuccessful) {
      log.warn("Authentication 2FA failed for user " + username);
    }
    // END --- Customized code for two factor authentication Part I --- END


    principal = realm.authenticate(username, password);

    // BEGIN --- Customized code for two factor authentication Part II ---
BEGIN
    if (principal == null || !twoFactorSuccessful) {
      log.warn("Authentication of '" + username + "' failed: [principal=" +
principal + "], [twoFactorSuccessful=" + twoFactorSuccessful + "]");
      forwardToErrorPage(request, response, config);
      return (false);
    }
    // END --- Customized code for two factor authentication Part II --- END

    if (log.isDebugEnabled()) {
      log.debug("Authentication of '" + username + "' was successful");
    }

    if (session == null) {
      session = request.getSessionInternal(false);
    }
    if (session == null) {
      if (containerLog.isDebugEnabled()) {
        containerLog.debug("User took so long to log on the session expired");
      }
      if (landingPage == null) {
        response.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT,
sm.getString("authenticator.sessionExpired"));
      } else {
        // Make the authenticator think the user originally requested
        // the landing page
        String uri = request.getContextPath() + landingPage;
        SavedRequest saved = new SavedRequest();
        saved.setMethod("GET");
        saved.setRequestURI(uri);
        saved.setDecodedRequestURI(uri);
        request.getSessionInternal(true).setNote(Constants.FORM_REQUEST_NOTE,
saved);
        response.sendRedirect(response.encodeRedirectURL(uri));
      }
      return (false);
    }

    // Save the authenticated Principal in our session
    session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);

    // Save the username and password as well
    session.setNote(Constants.SESS_USERNAME_NOTE, username);
    session.setNote(Constants.SESS_PASSWORD_NOTE, password);

    // Redirect the user to the original request URI (which will cause
    // the original request to be restored)
    requestURI = savedRequestURL(session);
    if (log.isDebugEnabled()) {
      log.debug("Redirecting to original '" + requestURI + "'");
    }
    if (requestURI == null) {
      if (landingPage == null) {
       
response.sendError(HttpServletResponse.SC_BAD_REQUEST,sm.getString("authenticator.formlogin"));
      } else {
        // Make the authenticator think the user originally requested
        // the landing page
        String uri = request.getContextPath() + landingPage;
        SavedRequest saved = new SavedRequest();
        saved.setMethod("GET");
        saved.setRequestURI(uri);
        saved.setDecodedRequestURI(uri);
        session.setNote(Constants.FORM_REQUEST_NOTE, saved);
        response.sendRedirect(response.encodeRedirectURL(uri));
      }
    } else {
      response.sendRedirect(response.encodeRedirectURL(requestURI));
    }
    return (false);

  }

  [original code omitted] 

}

Best regards,

Marco
--
Dipl.-Inf. Marco Bellavia 
Software Engineering

DENIC eG
Kaiserstraße 75-77
60329 Frankfurt am Main
GERMANY

Angaben nach § 25a Absatz 1 GenG:
DENIC eG (Sitz: Frankfurt am Main)
Vorstand: Helga Krüger, Andreas Musielak, Carsten Schiefner, Dr. Jörg 
Schweiger 
Vorsitzender des Aufsichtsrats: Thomas Keller
Eingetragen unter Nr. 770 im Genossenschaftsregister, Amtsgericht 
Frankfurt am Main

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to