Authenticating Vaadin-based applications

Note: Use HttpServletRequestListener instead of TransactionListener. Changing a window is also not suggested as it requires a page reload. It is better to change the content of the current main window with Window.setContent().

Introduction #

A common requirement that 99% of web application developers face is the authentication of their applications. The details of the requirement may vary from one application to another, but the one thing is common - web application must display some kind of login/password dialog and does not allow use of any resources/windows/dialogs until authentication succeeds. Another common thing - to be able to get the context (username) of the current user somewhere inside the application UI or logic, after the authentication. In this article we’ll review both things and show you a very simple way to add such functionality into your ITMill toolkit application.

The way we ask for the password #

From desktop applications we all know, that usual way to login is to display a modal dialog, asking to supply a login/password information, so typical idea that comes into our mind is to perform the same, especially, as ITMill toolkit provides excellent capabilities for modal dialogs. However, one security issue is hidden here - in ITMill, modal dialog “modality” function is handled by a client-side mechanism, e.g. in a web browser. So, if your application raises a modal window until it is authenticated (modal window blocks the input to the main contents and therefore does not allow user to work with the application) it can be hacked from client-side, so modality will be removed as well as security that was based on it.

So, we try a bit another way - ITMill toolkit has another great feature - possibility to have an unlimited number of application-level windows, not only the single one. Such windows can have names that can be used as part of URL to invoke directly the window you like. From the web point of view it looks like you just open various (different) URL’s, however, they are all served by a single application instance. Moreover, windows can be reconfigured dynamically at runtime, so we’ll use this mechanism for implementing the main topic of our article. Let’s go.

The application #

First, we’ll write a simple application, that is open to all the world. Application has one window, that is automatically main one (in terms of toolkit) and allow anyone to send an SMS to a mobile phone.

Here we do have a main window for the application - it displays a couple of text fields for entering a phone number and a short message plus a button to perform a message send procedure. For simplification of this article I’m not making any layout/theme enhancements here. Action listener from button “Send” also shows us a fake message instead of real job of sending an SMS.

#!java
public class SMSWindow extends Window
{
    TextField number = new TextField("Phone number");
    TextField text = new TextField ( "SMS Text");
    Button btnSend = new Button ("Send");


    public SMSWindow()
    {
        super("SMS Utility");
        setName ( "sms" );
        initUI();
    }

    private void initUI ()
    {
        addComponent ( number );
        addComponent ( text );
        addComponent ( btnSend );

        btnSend.addListener ( new Button.ClickListener() {
            public void buttonClick ( Button.ClickEvent event )
            {
                showNotification ( "Thank you, message has been sent !", "To: " + number.getValue () + " : " + text.getValue ());
            }
        });
    }
}

Here we have an application class. Note, that we also implemented the “application instance pattern” here, in order to be able to get access to our application instance from anywhere in the UI code. This pattern will also help us later.

#!java
public class SMSApp extends Application implements ApplicationContext.TransactionListener
{
    private static ThreadLocal<SMSApp> currentApplication =
            new ThreadLocal<SMSApp> ();

    public void init ()
    {
        getContext ().addTransactionListener ( this );
        setMainWindow ( new SMSWindow () );
    }

   public void transactionStart ( Application application, Object o )
    {
        if ( application == SMSApp.this )
        {
            currentApplication.set ( this );
        }
    }

    public void transactionEnd ( Application application, Object o )
    {
        if ( application == SMSApp.this )
        {
            currentApplication.set ( null );
            currentApplication.remove ();
        }
    }
    public static SMSApp getInstance()
    {
        return currentApplication.get ();
    }

}

So, here is the final application look in a browser:

Securing our application #

Our application works excellently, but it has one big disadvantage - it allows anyone to send the SMS, that can affect our pocket, as we do pay for every message sent ;) So let’s protect the application, so only authenticated users can use it.

Remember, that in the introduction chapter we talked about modal dialog pattern and its disadvantages in accordance to security? So, for avoiding this effect, we’ll not use modal dialogs but use windows instead.

First, we will design a new window class, that is responsible for the login form UI:

#!java
public class LoginWindow extends Window
{
    private Button btnLogin = new Button("Login");
    private TextField login = new TextField ( "Username");
    private PasswordField password = new PasswordField ( "Password");


    public LoginWindow ()
    {
        super("Authentication Required !");
        setName ( "login" );
        initUI();
    }

    private void initUI ()
    {
        addComponent ( new Label ("Please login in order to use the application") );
        addComponent ( new Label () );
        addComponent ( login );
        addComponent ( password );
        addComponent ( btnLogin );
    }
}
Now we want to perform actual authentication, when a login button is clicked. We also do not want to display main SMSWindow until authenticated, we also want that login window will appear automatically when application is started and user opens any application-related page in the browser. To do so, we’ll perform the following steps:

We’ll add the LoginWindow as the main window of our application in it’s init() method, instead of SMSWindow as it was initially:

#!java
public class SMSApp extends Application implements ApplicationContext.TransactionListener
{
    private static ThreadLocal<SMSApp> currentApplication =
            new ThreadLocal<SMSApp> ();

    public void init ()
    {
        setMainWindow ( new LoginWindow() );
    }
...

Now we need to handle the authentication process. As this procedure is application-centric, it is good practice to define the method inside the application class, instead of LoginWindow. So let’s add the authenticate() method into our application class, that takes login and password as parameters and either performs the authentication or throws an exception:

#!java
public void authenticate( String login, String password) throws Exception
    {
        if (  "user".equals ( login ) && "querty".equals( password ) ) 
        {
            loadProtectedResources();
            return;
        }
       
       throw new Exception("Login failed!");

    }

    private void loadProtectedResources ()
    {
        setMainWindow ( new SMSWindow () );
    }

Note, that we also added an extra utility method named loadProtectedResources(), that is responside for loading the rest of application resources - windows, dialogs, etc... If the supplied login and password are correct, this method is called. In our example we set the new main window to our application, thus replacing initial LoginWindow class. Having this extra method makes our application code a bit cleaner rather than if we put everything into authenticate() method.

The last 2 cents is to actually call the authenticate() method when user clicks a “Login” button in a login screen. This is easy to do inside the LoginWindow class and its initUI() method, where we add a listener for a login button. We will also use here our pattern for getting an application instance from everywhere:

#!java
        btnLogin.addListener ( new Button.ClickListener()
        {
            public void buttonClick ( Button.ClickEvent event )
            {
                try
                {
                    SMSApp.getInstance ().authenticate((String)login.getValue (), (String)password.getValue ());
                    open ( new ExternalResource (SMSApp.getInstance ().getURL ()));
                }
                catch ( Exception e )
                {
                    showNotification ( e.toString ());
                }
            }
        });

What do we do here ?

Firstly, we get the application instance and call our new authenticate() method. Secondly, if no exception happened, we use toolkit’s open(...) function to refresh the application page - at this moment main application window is already replaced (remember, what we did in authenticate() method ?) to a working SMS messenger window instance.

If an exception is thrown, we simply raise a notification message for the user and stay inside a login screen. No resources have been replaced, so even if user manually refreshes a browser, it will fall back to login screen.

Here is our application working - only a user/qwerty can login, any other will see a "Login failed" message:

Using user-context #

In some cases we may want to implement billing, so we can bill particular user that uses our application for SMS sending. In order to do this, we must know the user name that is performing an operation. So let’s extend our application to support this:

Firstly, we add a currentUser field into our application class. This field will store the currently logged-in user logon name and is null by default:

#!java
private String currentUser = null;

Secondly, in authenticate() method, after validating login name and password, we set this field:

#!java
public void authenticate( String login, String password) throws Exception
    {
        if ( !"user".equals ( login ) || !password.equals ( "qwerty" ))
        {
            throw new Exception ("Login failed !");
        }
        
        currentUser = login;
        loadProtectedResources();
    }

Thirdly, we add a getter in order to let other components of our application get the current user name:

#!java
public String getCurrentUser()
{
   return currentUser;
}

Now, after every successful login, currentUser field is filled with the user login name and anyone in this application context can use it for accounting, auditing and any other purposes by invoking this getter.

Multi-window applications #

As we mentioned in the introduction, ITMill toolkit allows us to have many windows that belong to the same application. Each window is allowed to be invoked by its name, by appending the name to the base application url, such as: http://localhost/myapp/window1 , http://localhost/myapp/window2 and so on... This is a very useful feature, but how can we handle this in our secured environment to avoid security holes, if user will start application by invoking a particular window URL instead of main application URL? That’s pretty easy - simply postpone creation of all but Login window until the authentication is done, e.g. move all application windows creation code into the loadProtectedResources() method. Thus, until authenticated, only one window will be registered with toolkit - the LoginWindow. Therefore, any URL’s, pointing to our application and extended with specific window names will anyway fallback to login screen.

5 Attachments
60543 Views
Average (16 Votes)
Comments