How to save an authenticated user in JASPIC?
Asked Answered
N

1

4

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.

Nisen answered 5/5, 2015 at 10:7 Comment(0)
M
6

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.

Macilroy answered 5/5, 2015 at 21:59 Comment(16)
Thanks, Arjan, I know you are very knowledgeable on this subject (found a lot of blog posts from you) and was looking for a way to reach out. I'm glad you found me. :)Nisen
@Nisen you're welcome, hope the answer is helpful ;)Macilroy
Very helpful, I'm glad I'm not the only one thinking this is an omission in the current specs, I'll cast my vote.Nisen
Sadly, it doesn't seem to work (Glassfish 4), so I'll stick with manually adding it in the session.Nisen
@Nisen do you use GF 4.0 or 4.1? We use it with 4.1 and it works. I remember an earlier version having a bug where it didn't work.Croton
I'll check, but maybe I'm doing something wrong? I tried inserting the registerSession bit right before exiting (return AuthStatus.SEND_SUCCESS in my case because I'm doing a redirect from within the validateRequest). It just goes back to the auth page and nothing was saved.Nisen
@Nisen yes, in that case it, unfortunately, doesn't work. registerSession only works when you actually authenticate for the current request. SEND_SUCCESS is btw not a valid return code for a redirect, but most JASPIC implementations don't actually check the return code for any other value than SUCCESS. There's an issue for your case and a possible workaround, see java.net/jira/browse/JASPIC_SPEC-23 but probably just storing in the session manually is easier for you.Macilroy
@ArjanTijms I have a virtual endpoint to handle a SAML Response. It's actually handled by the SAM in validateRequest (just checks whether I have a SAMLResponse parameter). If I use SUCCESS, I either get a 404, because it tries to continue to my non-existing virtual endpoint, or I get a 500 IllegalStateException after I redirect to my secured servlet because it tries to output something to (I presume) the same response which has redirected to it. SEND_SUCCESS makes sure the validateRequest response is the only one and it doesn't go any further.Nisen
@Nisen yes, you are right you cannot use SUCCESS here. That would "invoke the service" (continue to non-existing endpoint). What I meant was that officially validateRequest when called before the service invocation should only return SEND_CONTINUE (e.g. when redirecting) or SEND_FAILURE (e.g. when an internal exception occurred). In practice, most JASPIC runtimes don't look at the status codes. They only check for "SUCCESS or [anything else]". See section 3.8.3.1 of spec. As experiment, just try to return SEND_CONTINUE. Likely the exact same thing will happen ;)Macilroy
I still have doubts, @ArjanTijms, SEND_SUCCESS says: "Indicates that the message processing by the authentication module was successful and that the runtime is to proceed by sending a message returned by the authentication module." It seems to me that I should use this one instead of SEND_CONTINUE after I verified the SAML Response. Effectively this is saying: Message processing is complete, but use my response instead of the virtual servlet you're calling and it just happens to be a redirect.Nisen
@ArjanTijms: Is there any way to reach you privately so we can continue this conversation?Nisen
@Davio, you can ping me on my blog if you want (arjan-tijms.omnifaces.org), but SO can also move this discussion here to chat. That's probably the easiest way.Macilroy
@Nisen I have to agree that the return values are not overly clear and I have to look them up and read the description of each a couple of times over just as well. My advice would be to not just look at the Javadoc, but at the spec document here: jcp.org/aboutJava/communityprocess/mrel/jsr196/index2.html look at 3.8.3.1 (page 33). It only lists 3 options apart from SUCCESS, which is 1. SEND_CONTINUE, 2. SEND_FAILURE, 3. throwing an exception.Macilroy
Let us continue this discussion in chat.Macilroy
Thanks for the post and the comments Arjan. This was a step I was looking for.Alkalify
@Arjan, I use 'javax.servlet.http.registerSession' to remember the principles. Initially I check if the principle exist and if so call the handler and I'm done. But when the session expires the principle is still there and the request is allowed. So what I need is that the 'remembered' principles must be removed when the session expires. Can you tell me how to do this? Or should I simply first check if the session is there?Gwenn

© 2022 - 2024 — McMap. All rights reserved.