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
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]