5 Replies Latest reply: Nov 22, 2012 2:12 AM by 856652 RSS

    "Remember me"/"Stay logged in" feature at login page

    856652
      Hi,
      I have a login page and I want there an option like "Remember me" so that a user doesn't have to enter user name and password everytime he wants to access the website. We are Developing with Jdev PS4.

      Login.java
      public void doLogin(ActionEvent actionEvent) {
         FacesContext ctx = FacesContext.getCurrentInstance();
         HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
         CallbackHandler handler = new URLCallbackHandler("my user", "my password");
         Subject mySubject = Authentication.login(handler);
         ServletAuthentication.runAs(mySubject, request);
         String loginUrl = "adfAuthentication?success_url=/faces/...";
      
         ...
      
         ctx.getExternalContext().redirect(loginUrl);
         ctx.responseComplete();
      }
      web.xml:
        <login-config>
          <auth-method>FORM</auth-method>
          <form-login-config>
            <form-login-page>/faces/myLogin.jspx</form-login-page>
            <form-error-page>/faces/welcome.jspx</form-error-page>
          </form-login-config>
        </login-config>
      I also started testing with a cookie after successfull login:
              final FacesContext facesContext = FacesContext.getCurrentInstance();
              final ExternalContext externalContext = facesContext.getExternalContext();
              final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
              final HttpSession session = (HttpSession) externalContext.getSession(false);
      
              //Extend session timeout
              session.setMaxInactiveInterval(REMEMBER_ME_TIMEOUT_IN_S);
      
              //Generate session ID
              final String sessionId = UUID.randomUUID().toString();
              
              //Create persistent cookie
              final Cookie cookie = new Cookie(REMEMBER_ME_ID_KEY, sessionId);
              cookie.setMaxAge(REMEMBER_ME_TIMEOUT_IN_S);
              cookie.setPath("/");
              //cookie.setSecure(true); //Only send when SSL ON
              response.addCookie(cookie);
      But now I have some questions how to continue:
      1. How to skip the login page when the user accesses the webpage? Use a HttpSessionListener or Filter to redirect? Can someone provide a snippet how to implement best?
      2. How to perform the user login, because I don't want to save the credentials in the cookie. How can I perform the login as shown above: CallbackHandler handler = new URLCallbackHandler("my user", "my password");

      Any hints/links or examples to help get me get started on adding such a feature. would be more than appreciated.

      Regards Markus
        • 1. Re: "Remember me"/"Stay logged in" feature at login page
          codigoadf
          Hi,
          another posibility is PagePhaseListener.
          you can authenticate the user through ServletAuthentication.runAs and URLCallbackHandler, see sample here: http://andrejusb.blogspot.com.es/2010/11/things-you-must-know-about-adf-faces.html
          and then redirect to welcome.jspx
          • 2. Re: "Remember me"/"Stay logged in" feature at login page
            Frank Nimphius-Oracle
            ... this would however require storing username and password in a cookie. So ideally you create some sort of save password recovery in that you maintain encrypted table data to store username and password and associate it with a token that you send along in the cookie. With the username and password, make sure you also register the remote host so that nobody can login to the application by stealing the cookie

            Frank
            • 3. Re: "Remember me"/"Stay logged in" feature at login page
              856652
              Ok, thanks, I will try the PagePhaseListener and also remember the remote host. Then I will give you a feedback if it is working as expected.
              • 4. Re: "Remember me"/"Stay logged in" feature at login page
                856652
                Which phase should I use to do my redirection if cookie is available? Could the impl. look like this:
                public class CustomADFPhaseListener extends ADFPhaseListener
                {
                    @Override
                    protected void executePhase(final Lifecycle.Phase phase, final LifecycleContext lifecycleContext)
                    {
                        if (phase.equals(Lifecycle.???????????????????))
                        {
                            final boolean authenticated = ADFContext.getCurrent().getSecurityContext().isAuthenticated();
                            if (!authenticated) {
                                final ExternalContext externalCtx = FacesContext.getCurrentInstance().getExternalContext();
                                final HttpServletRequest request = (HttpServletRequest) externalCtx.getRequest();
                                ... //Check cookie, get username/password and login
                            }
                        }
                        super.executePhase(phase, lifecycleContext);
                    }
                }
                Or should I use the oracle.adf.controller.v2.lifecycle.PagePhaseListener and what phase to use here? Which method: afterPhase/beforePhase?

                Thanks in advance, Markus

                Edited by: 853649 on 20.11.2012 02:33
                • 5. Re: "Remember me"/"Stay logged in" feature at login page
                  856652
                  Ok, now I have a working approach (as far as I can rate this for now).

                  I use a PagePhaseListener to check if there is the persistent cookie with a unique ID. With the unique ID at server side I can restore the user credentials and perform a login.

                  PagePhaseListener:
                  public class RememberUserPagePhaseListener implements PagePhaseListener {
                      private static final String LOGIN_URI = "/context/faces/login";
                  
                      @Override
                      public void beforePhase(final PagePhaseEvent pagePhaseEvent)  {
                          if (pagePhaseEvent.getPhaseId() == Lifecycle.PREPARE_MODEL_ID)   {
                              final ExternalContext externalCtx = FacesContext.getCurrentInstance().getExternalContext();
                           final HttpServletRequest request = (HttpServletRequest) externalCtx.getRequest();
                           final String requestUri = request.getRequestURI();
                           if (requestUri.startsWith(LOGIN_URI)) {
                                final boolean authenticated = ADFContext.getCurrent().getSecurityContext().isAuthenticated();
                                   if (!authenticated) {
                                     //Check if there is a cookie
                                     final Cookie cookie = (Cookie) externalCtx .getRequestCookieMap().get("my.rememberme.cookie");
                                     String cookieId = null;
                                     if (cookie != null)
                                     {
                                             cookieId = cookie.getValue();
                                         //With this cookie ID you can get stored user data from memory/database/... (see below)
                                      //Also compare host if cookie is valid
                                      //Then perform a normal login as seen in my first post
                                             .....
                                 
                                        }
                                }
                              }    
                          }
                      }
                  
                      @Override
                      public void afterPhase(final PagePhaseEvent pagePhaseEvent) {
                      }
                  }
                  The PagePhaseListener must be registered in "adf-settings.xml". In JDev 11.1.1.4 there is an error for tag "adfc-controller-config", but it works. Seems to be a JDev Bug
                  <?xml version="1.0" encoding="windows-1252" ?>
                  <adf-settings xmlns="http://xmlns.oracle.com/adf/settings">
                      <!-- Error in JDEV, he says this is not a valid tag but it works as expected -->
                      <adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">
                          <lifecycle>
                              <phase-listener>
                                  <listener-id>RememberUserPagePhaseListener</listener-id>
                                  <class>com.xxx.xxx.xxx.RememberUserPagePhaseListener</class>
                              </phase-listener>
                          </lifecycle>
                      </adfc-controller-config>
                  </adf-settings>
                  How to create a persistent cookie:
                          final FacesContext facesContext = FacesContext.getCurrentInstance();
                          final ExternalContext externalContext = facesContext.getExternalContext();
                          final HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
                          final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
                          final String cookieId = UUID.randomUUID().toString();
                          final Cookie cookie = new Cookie("my.rememberme.cookie", cookieId);
                          cookie.setMaxAge(14 * 24 * 60 * 60);  //Two weeks
                          cookie.setPath("/");
                  
                          //Remember username, password and remote host for this random cookie ID. This will be used in the PagePhaseListener
                          final String remoteHost = request.getRemoteHost();
                          ...
                  Remember to delete the cookie if perform a logout (create the same cookie with max age of 0). Also you must schedule a job which will cleanup the map at some time for cookieIds which are created a long time ago.