Tuesday 2 April 2013

[How To] - Redirect to original page after login in JSF

Nowadays, redirecting users to where they were before logging is a common & desired feature for any web applications. For instance, if a user was surfing a page at http://example.com/yourapp/pageX.xhtml?param1=value1, the application should be able to redirect the user to the same page and, at the same time, retain the complete query string at the end of the requestURL.

In this tutorial, I will assume that your application has a login bar on top of all pages. Based on this assumption, your application needs to handle the following 3 scenarios:
  1. The user logs in directly from the top login bar and succeed.
  2. The user entered wrong password, etc.
  3. The user was forwarded or redirected to the login.xhtml page when they tried to access a restricted resource.
For the first scenario, one very important thing to note is that when a user clicks the Login button, the requestURL for that click will not carry the original query string anymore. As a consequence, if you capture the requestURL at this point, it's too late and you will only have http://example.com/yourapp/pageX.xhtml. In other words, to get the full requestURL, you need to capture the requestURL even before the user clicks the Login button. To achieve this goal, in your templates, you need to define the following <f:event>:

This is our  @SessionScoped managed bean:

And this is the Login managed bean:

For the second scenario, users will usually arrive at the login.xhtml page where you display a big & fat error message. At this point, you might have jumped into your own trap if you have also included the above <f:event> inside your login.xhtml page. The event would be triggered and your originalURL property would be wrongly updated to the login page's URL. So, in brief, you must NOT include the above <f:event> inside your login.xhtml page.

Done? No, this is a double-trap :O!  We still need to take care of the situation in which the user didn't surf any pages before logging in.

For the third scenario, you need to understand the difference between a forward and a redirect. When a user is forwarded to the login.xhtml page, the requestURL will still be http://example.com/yourapp/pageX.xhtml?param1=value1 even though the content of the view is from the login.xhtml page. On the other hand, if the user is redirected, the requestURL will become http://example.com/yourapp/login.xhtml.

In case the user was redirected, I will assume that you're using a homegrown Filter to perform the login check and redirect users afterward. In this situation, you need to include the original requestURL as a parameter in the redirecting URL:

After that, you need to update your LoginBean#init() function to retrieve the parameter in both cases as following:

Finally, after spilling much sweat, you can enjoy your work now! :)

4 comments:

  1. I am using the same example of using socialauth for facebook login for jsf web application. However i am not able to execute the application successfully because of the below issue.
    {
    "error": {
    "message": "redirect_uri isn't an absolute URI. Check RFC 3986.",
    "type": "OAuthException",
    "code": 191
    }
    }

    Please help me in resolving this issue. Provide me your mail id to send the complete WAR file for you to look into

    ReplyDelete
    Replies
    1. SocialAuth will generate a proper link for you to redirect the user to for logging in. I think you should try to print out the link to debug :)... And sorry, at the moment, I'm a bit busy. Please upload your WAR to some repository and leave the link here. I will take a look when I have more spare time :D...

      Delete
  2. Thanks for posting this. I've been wanting to switch to container based authentication but really don't have time. Your post has allowed me to tweak my existing filter based authentication to redirect users to the URL they initially tried to visit.

    I do have a problem when people try to visit http://example.com/myapp. This takes them to the login page if they aren't already logged in. But the originalURL is set to /myapp/ so they get taken back to the login page even after a successful login. After they login a second time, they are taken to the welcome page. I'm trying to figure out a tidy way to avoid this, probably by adding some extra checks in my filter class, for instance by checking that requestURI != contextPath (plus an extra slash).

    ReplyDelete
    Replies
    1. I think the simplest solution would be not to include the "" in any pages that do not require redirecting to. For instance, the Login page should not contain the event. The effect is that no "originalURL" will be captured.

      Delete