- - - -AppFuse includes a Remember Me feature that works with Container-Managed Authentication. In version 1.3 it works by setting a few cookies: username, password and rememberMe. The last one being a simple flag that the user wants to be remembered. Then a LoginFilter checks for the rememberMe cookie, and if present, logs in the user using the other cookie values. The obvious issue with this is that the password being sent and stored on the user's browser.

This was easily solved in Tomcat 4 by placing the form-login-page and form-error-page under a "security"
directory and then setting cookies on the /appfuse/security path. This way, since no other part of
the app can access /appfuse/security, these cookies can never be retrieved in any part of the application.
The problem is that this didn't work in Tomcat 5 since it forwards to the login page (rather than redirecting).
Since forwarding is obviously a better solution (user's can't bookmark the login page), I needed a new way to
implement the Remember Me feature.

To my knowledge, cookies can only be stolen if someone is able to login to your AppFuse app and insert JavaScript
to send the "document.cookie" value to an external URL. So for AppFuse, it's likely that stealing cookies is not
much of an issue. However, for applications like Roller, it is an issue - since other bloggers on the same server
(i.e. JRoller) could put JavaScript on their blog to grab cookies from other users.

Just as I was about to give up searching for solutions, along came
Charle's persistent cookie strategy. Here's
how I implemented it in AppFuse. Hopefully it follows all the rules and is a good solution. Here's what I did make it happen.

- - - -Step 1: Setting the cookie.Scenario: A user logs in and selects the "Remember Me" checkbox.What Happens: When a user clicks the Login button, they submit to a LoginServlet that redirects them to "j_security_check" to take
advantage of Container-Managed Authentication. This servlet is responsible for ensuring an SSL Login (if enabled), encrypting the
user's password (if enabled) and also sets a session variable to indicate the user wants to be remembered. After authenticating, the user
will hit the ActionFilter, where the following code sits:

The RandomGUID is a class I found on Java Exchange. Once the rememberMe cookie was set, I had to
configure LoginFilter.java (mapped to form-login-page and form-error-page) to look for this cookie. This brings us to Step 2.

- - - -Step 2: Using the cookie to login the user.Scenario: A User has already logged in successfully with "Remember Me" enabled.What Happens: When the login page is served up to the user, the LoginFilter is invoked and it checks the validity of the "Remember Me" cookie.

The UserManager.checkLoginCookie(value) method looks up a record based on the random GUID, and if it finds a match, it creates a new GUID and saves it in the database. If null is returned, it means the cookieId doesn't exist, and the login proceeds as it normally would. Below is the guts of the checkLoginCookie() method.

You can see from this, that if the lookup succeeds - a new cookieId is saved and returned. If a not-null cookieId is returned, the remember me cookie is updated, the user is looked up and the Filter forwards an authentication request (with username/password) to the LoginServlet. The Filter also sets an attribute to let the application know that this user authenticated via cookies. This is important so that cookie-authenticated users cannot change passwords. When using cookie-authentication, the password field is hidden and a message warns the user that they must logout/login to change passwords.

Lastly, I had to come up with a solution to remove these login cookies.

- - - -Step 3: Allow the user to clear their login cookies.Scenario: A User has already logged in successfully with "Remember Me" enabled.What Happens: For this, I implemented a simple solution. When a user logs out, all persistent login cookies are removed.

I don't know if it's best to divulge the details of AppFuse's cookie login strategy. However - it *is* open source - so folks can find figure it out if they really want to. By exposing it to the world, I hope to get the most robust solution possible.

Next up, how I replaced Hibernate with iBatis. Using Spring, it only took me a few hours! Pretty slick, eh? ;-)

Just my 2 cents : you say "When using cookie-authentication, the password field is hidden and a message warns the user that they must logout/login to change passwords." - an alternative approach would be to require a user to /always/ enter their old password on the 'Enter your new password screen'. Although it's a bit anal retentive, maybe that way is a bit simpler and also a bit more secure?
Roberto

You could use the JDK1.5 classes now for the GUIDs... check in the java.util package I believe. They are probably a bit bigger than required (128 bits), but you could still trim them. I prefer using java packages instead of outside stuff, so it is something to think about for a jdk1.5 release...