Changing credentials without logging in again in Seam 1.2.1

13 June 2007

Nicolas Leroux

by Nicolas Leroux

This article explains how to do programmatic log in, in a Seam 1.2.1 application. If like us, you want to allow your users to change their credentials once they are logged in into your application, you might be forced to log them out and to ask them to re-login. There is a nicer way to do this.

In our case, each user is identified by his e-mail address and a password. What happens if your user wishes to change his e-mail address? He should be able to stay logged in after this operation. However, Seam 1.2.1 doesn’t allow to do so out of the box yet.

However, there is an open issue in JIRA, that suggest that will be possible in the future:

http://jira.jboss.com/jira/browse/JBSEAM-89: Support re-setting the principal in identity without re-login

In the meantime, we should be able to work around this issue. After having looked at the source code, I did find a way to implement this. Here are the relevant parts of the code.

In your Authenticator component add the following method:

/**
 * Do programmatic log-in for the given person.
 */
public void reloginAs(final Person person) {

    // Log out sets the principal to null and invalidates the session,
    // but we don't want to throw away our session.
    identity.logout();
    Contexts.getSessionContext().set("org.jboss.seam.sessionInvalid", false);

    // However, we need to clear the security from memory because the user
    // principal changed (it is now person.getMailAddress).
    // This will also recreate the security rules and reset the Subject
    identity.resetPrincipals();

    // Remove the previous one
    identity.setRememberMe(true);
    log.info("New e-mail address [" + person.getMailAddress() + "]");
    identity.setPassword(person.getPassword());
    identity.setUsername(person.getMailAddress());

    // We don't want to display the log in welcome message and we don't want
    // to see it until we have a new Identity object anyway
    // That's not really nice, but that's efficient ;)
    identity.setDisplayLoginMessage(false);

    // This will propagate the new user principal
    identity.login();
}

Now add your own Identity component that extends the JBoss Seam one:

/**
 * Replacement for the built-in "identity" component.
 */
@Name("org.jboss.seam.security.identity")
@Scope(value = ScopeType.SESSION)
@Install(precedence = Install.DEPLOYMENT)
public class Identity extends org.jboss.seam.security.RuleBasedIdentity {

    private boolean displayLoginMessage = true;
    private static final long serialVersionUID = -2692787306570157411L;

    public Identity() {
        isPasswordHash = false;
    }

    public void setDisplayLoginMessage(final boolean display) {
        this.displayLoginMessage = display;
    }

    @Override
    protected void addLoginSuccessfulMessage() {
                // When you logout and re-login a new identity instance will
                // be created and therefore the welcome message will be shown.
        if (displayLoginMessage) {
            super.addLoginSuccessfulMessage();
        }
    }

        /**
         * This one is used to reset the principal and
         * call two protected methods from this class.
         */
    public void resetPrincipals() {
        super.unAuthenticate();
        super.create();
    }

}

Et voila, using the reloginAs method, you can now allow your users to change their credentials without logging them out.