1 Reply Latest reply on May 19, 2010 6:29 AM by Vaughn Butt

    Here's how to support log-in and log-out even when the view has expired

    Stephen Friedrich Novice

      Here's how to support log-in and log-out even when the view has expired

      Put mildly, I think it is very annoying to click on a log-out and be answered
      with an error message that tells me that I have to log-in first.
      Trying to log-in and being shown the log-in page again because the previous view
      state has already expired is an equally bad user experience.

      I have been struggling with these issues and finally found solutions.
      Here they are. They might be helpful for somebody else or there might be a
      better/easier way to achieve the same.

      Note that there is a facelets parameter, that should  automatically provide
      the desired behaviour, but on first attempt it didn't work for me and the bug
      mentioned near the end of this thread sounds daunting:

      Log-out with expired view

      This is easy to handle using Seam:
      Just make the log-out link a plain (GET) link (instead of a command link):

      <h:outputLink value="#{request.contextPath}/log-out" rendered="#{identity.loggedIn}">

      and handle the actual log-out in a page action (pages.xml):

      <page view-id="/log-out.xhtml">
         <rewrite pattern="/log-out"/>
         <action execute="#{authenticator.logout}"/>

      where authenticator.logout is simply

         public String logout() {
            return null;

      Instead of using an outputLink with a page action it would probably
      work just as well to use <s:link action="#{authenticator.logout}"...

      Log-in with expired view

      This one is a little tougher: You can't use a GET request, because naturally
      passwords should never be part of the URL. However a JSF postback always
      requires the view state to be restored.

      So I fell back to writing the log-in form in plain html. You can actually use
      JSF components for input fields and labels, but use a plain html form tag
      instead of h:form, so that the action can be customized and the POST request
      handled outside of JSF.

      This is in /log-in.xhtml:

      <form method="post" action="/acme/do-log-in" >
           <h:inputText id="nameInput"/>
           <h:inputText id="passwordInput"/>
           <h:inputHidden id="cid" value="#{conversation.id}"/>
           <h:commandButton id="loginButton" value="Login"/>

      Labels, messages, and layout excluded for brevity.
      Be sure to pass the conversation id in a hidden field (the name of that parameter is configured in components.xml: <core:manager ... conversation-id-parameter="cid"/>)

      To handle the POST request my first attempt was a custom servlet (integrated
      in Seam by configuring the ContextFilter), but that did not work because the
      events and redirects that occur after a login attempt require an active

      But fortunately a Seam page action can handle POST requests, too:

         <page view-id="/do-log-in.xhtml" action="#{authenticator.doLogIn}">
            <rewrite pattern="/do-log-in"/>
            <navigation from-action="#{authenticator.doLogIn}">
               <rule if="#{not identity.loggedIn}">
                  <redirect view-id="/log-in.xhtml"/>
               <rule if="#{identity.loggedIn and empty redirect.viewId}">
                  <!-- Either the user hit the log-in page directly, or the session has expired while the log-in page sat idle -->
                  <redirect view-id="/contact-data.xhtml"/>

      and the Authenticator.doLogIn() just grabs the request values, validates them
      (we are bypassing JSF, so no help from JSF validators), stuffs them into the
      credentials and calls identity.login():

          @In private Credentials credentials;
          @In private FacesMessages facesMessages;
          @In private Identity identity;
          public void doLogIn() {
              boolean valid = true;
              String userName = FacesUtil.getRequestParameter("nameInput");
              if (Stringz.isEmpty(userName)) {
                  valid = false;
                  facesMessages.addToControl("nameInput", StatusMessage.Severity.ERROR, "Please enter a value");
              String password = FacesUtil.getRequestParameter("passwordInput");
              if (Stringz.isEmpty(password)) {
                  valid = false;
                  facesMessages.addToControl("passwordInput", StatusMessage.Severity.ERROR, "Please enter a value");
              if (!valid) {
              String result = identity.login();

      When testing the validation, you'll see that the faces messages are not added to
      the controls, but will end up as global messages. That is a known bug in Seam:


      I just don't really want to think about the fact that these are problems that
      never would have happened if I used plain JSP pages. JSF comes at a cost.

      (Then again it was not so much harder than getting all the formatting instructions for this forum message right.)