javasecurityauthenticationjaspicjsr196

How to save an authenticated user in JASPIC?


I have developed a Security Authentication Module (SAM) and implemented the validateRequest method. I also have a simple webapp configured to use this SAM.

In my validateRequest method, I check the clientSubject and set a CallerPrincipalCallback with a hardcoded username and a GroupPrincipalCallback with a hardcoded group name:

final CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, "anonymous");
final GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(clientSubject, new String[] {"user"});

try {
  this.handler.handle(new Callback[] {callerPrincipalCallback, groupPrincipalCallback});
} catch (IOException | UnsupportedCallbackException e) {
  logger.error(e.getMessage());
}

I noticed that everytime I refresh a servlet in my webapp, the client subject is simply blank, logger.debug("Client: {}", clientSubject);:

2015-05-05 11:21:02,200 DEBUG n.m.j.s.Saml2AuthModule [http-listener-1(2)] Client: Subject:

Is it possible to "save" a subject somehow so that the subject is attached to the session and I can simply skip logging in the same user every time?

EDIT I think I found a way by manually storing it in the HttpSession: req.getSession().setAttribute("subject", user); Not pretty, but it works.


Solution

  • Is it possible to "save" a subject somehow so that the subject is attached to the session and I can simply skip logging in the same user every time?

    Yes, although JASPIC was designed to be stateless, it does have an option to semi-automate remembering the login.

    This option is however not really much less code than just storing the details in the session and re-authenticating at the start of each request.

    The way to do this is first setting a boolean in the message info map before returning SUCCESS and exiting validateRequest:

    messageInfo.getMap().put("javax.servlet.http.registerSession", TRUE.toString());
    

    Then at the start of every request your authentication module (SAM) is still called, but you can execute the following "protocol" to re-use the stored identity data (username + roles):

    HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
    Principal userPrincipal = request.getUserPrincipal();
    
    if (userPrincipal != null) {   
        handler.handle(new Callback[] { 
            new CallerPrincipalCallback(clientSubject, userPrincipal) }
        );
    
        return SUCCESS;   
    }
    

    I wrote a blog entry with some more details. You can find a test that uses a fully working example in the Java EE 7 samples project.

    There's unfortunately no functionality in JASPIC to say that you don't want to have the SAM called at all as long as the (http) session is valid.

    If you care for this feature, then please vote for the following issue: https://java.net/jira/browse/JASPIC_SPEC-20

    I think I found a way by manually storing it in the HttpSession: req.getSession().setAttribute("subject", user); Not pretty, but it works.

    More or less the "official" way is to store the username and roles inside the session and then at the beginning of each call to validateRequest check if this data is there and if so hand it over to the two callbacks.

    The method I showed above is not really unlike that, but apart from the obvious differences (one callback vs two, getting the principal from the request vs getting it from the session) the major difference is that via the semi-automatic way the container is free to use whatever mechanism it has to store the data.

    This may be just an attribute in the session again (a simple JASPIC implementation could surely do that), or it could use some hidden part of the session that most containers have. This hidden part is not directly accessible to user code, which may have some advantages.