jakarta-eeglassfishjaspic

custom ServerAuthModule (loginModule), where to hash the password? JASPIC


I'm trying to implement a loginmodule so I can perform a "remember me" feature on my web app as well as hash my password with bcrypt. To build the class I used this tutorial. However I didn't manage to connect after implementing this. The passwords in db are hashed via SHA-256 at the moment and I suspect it is the reason why.

  public class TestAuthModule implements
        javax.security.auth.message.module.ServerAuthModule {

    @SuppressWarnings("rawtypes")
    protected static final Class[] supportedMessageTypes = new Class[] {
            HttpServletRequest.class, HttpServletResponse.class };

    private CallbackHandler handler;

    public void initialize(MessagePolicy requestPolicy,
            MessagePolicy responsePolicy, CallbackHandler handler,
            @SuppressWarnings("rawtypes") Map options) throws AuthException {
        System.out.println("initialize called.");
        this.handler = handler;
    }

    @SuppressWarnings("rawtypes")
    public Class[] getSupportedMessageTypes() {
        return supportedMessageTypes;
    }

    public AuthStatus validateRequest(MessageInfo messageInfo,
            Subject clientSubject, Subject serverSubject) throws AuthException {
        HttpServletRequest request = (HttpServletRequest) messageInfo
                .getRequestMessage();

        String user = request.getParameter("user");
        String group = request.getParameter("group");

        System.out.println("validateRequest called.");
        System.out.println("User = " + user);
        System.out.println("Group = " + group);

        authenticateUser(user, group, clientSubject, serverSubject);

        return AuthStatus.SUCCESS;
    }

    public AuthStatus secureResponse(MessageInfo msgInfo, Subject service)
            throws AuthException {
        System.out.println("secureResponse called.");
        return AuthStatus.SEND_SUCCESS;
    }

    public void cleanSubject(MessageInfo msgInfo, Subject subject)
            throws AuthException {
        if (subject != null) {
            subject.getPrincipals().clear();
        }
    }

    private void authenticateUser(String user, String group,
            Subject clientSubject, Subject serverSubject) {
        System.out
                .println("Authenticating user " + user + " in group " + group);

        CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(
                clientSubject, user);

        GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(
                clientSubject, new String[] { group });

        try {
            handler.handle(new Callback[] { callerPrincipalCallback,
                    groupPrincipalCallback });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

and I login like this (which did work before implementing a custom loginmodule):

private String username;
private Password password;
//....

for (int i = 0; i < x -1 ; i++) {
    this.password = PasswordEncoder
                       .toHex(PasswordEncoder
                            .hash512(this.password + salt));
    }
   // x is the number of time I hashed the password before storing it in db.
   // x-1 because glassfish authentication does it once for me.

   //...
try {
    request.login(username, password + salt);
    } catch (ServletException e)

Also on my pages I used to have a register and a sign in button that were displayed only if the user was null if not I had the username at the top. Now that I implemented this it's like the user is connected as "ANONYMOUS" (so there is "you are connected as ANONYMOUS" at the top of the page. To prevent this I did a temporary fix:

    if (username == null || username.equals("ANONYMOUS")) {
        this.isUserConnected = false;
    } else {
        this.isUserConnected = true;
    }

I tried :

isUserInGroup("ANONYMOUS"); 

but there is no user so I'm getting a npe. I'm not sure how to go about this as well.


Solution

  • There are two alternative approaches you can employ here.

    The first --which I personally favor-- would be to just forget about proprietary AS-provided JAAS LoginModules (LMs) altogether and implement the authentication process in its entirety yourself, within your ServerAuthModule's (SAM's) validateRequest method. This gives you the ultimate freedom of choice with regard to how credentials are to be persisted (hashing/salting, DB schema) by the application and how the SAM shall perform validation of client-supplied credentials. Hence, the SAM is responsible for connecting to the DB in both a thread-safe and efficient manner in that case; that would be the sole potentially tricky aspect of this approach.

    Alternatively, your SAM can delegate credential validation to an AS-provided LM (or to one you authored, provided that it is applicable, i.e. a vendor-specific extension). That's what you've been trying to accomplish, I suppose. The SAM must then comply with both JASPIC's Servlet Container Profile and its LoginModule Bridge Profile (see chapter 6 of the spec); the latter, in a nutshell, mandates that:

    1. The SAM's initialize method consults the value of the javax.security.auth.login.LoginContext key encapsulated within the runtime-provided options. It also constructs an additional CallbackHandler that supports NameCallback and PasswordValidationCallback. Finally, it instantiates a request-specific LoginContext (LC) with the aforementioned instances (name and callbackHandler arguments, respectively).
    2. Its validateRequest delegates to LC's login; then communicates any Principal(s) established by the underlying LM within LC's Subject to the AS (via CallerPrincipalCallback and GroupPrincipalCallback).
    3. Its cleanSubject delegates to LC's logout.

    Obviously, unless you wrote the LM yourself, no clean way of altering the credential validation process is available.

    Some closing notes:

    See also: