Skip to content

Design Patterns for Authentication

Jim Brucker edited this page Apr 30, 2015 · 4 revisions

Assignment

Apply design patterns to how your application handles logins so that the authentication mechanism can change without affecting your code.

To do this exercise, create a branch or fork of your eXceedVote project. This is an experiment, so don't work on the master branch.

Identify the Responsibility to Reassign

Most ExceedVote projects have a route to handle logins (from a view) something like this:

GET    /login                     controllers.Application.login()
POST   /authenticate              controllers.Application.authenticate()

When the user submits (POSTs) the login view it invokes Application.authenticate() which does this:

public static Result authenticate( ) {
    Form<Login> loginForm = Form.form(Login.class);
    loginForm = loginForm.bindFromRequest();
    if (loginForm.hasErrors()) {
         return badRequest(views.html.login.render(loginForm));
    }

    Login data = loginForm.get();
    // authenticate the username using password
    // if this fails it returns null
    User user = User.authenticate( data.username, data.password);
    if (user == null) {
        flash("error", "Username or password incorrect.");
        return badRequest(views.login.render(loginForm));
    }
    // success! Create a session for this user.
    . . .
}

In this code, the responsibility for authenticating users is handled by the User class. Since its in the User class, its difficult to change without modifying the code for User.

  1. To enable the implementation of authentication to change, let's reassign the responsibility to a new class named Authenticator:
public class Authenticator {
    public User authenticate(String username, String password);
    . . .
}

QUESTION: What design principles (GRASP or other) are being used here?

  1. Just reassigning the job to Authenticator doesn't help much. We need a way to vary the authenticator. If we use new to create an Authenticator, such as:
// in Application.authenticate (controller)...
   Authenticator auth = new Authenticator();
   User user = auth.authenticate(username, password);
   if (user == null) ...

then we'd have to modify the code in the authenticate method in order to change authentication.

A good mechanism to manage object creation is the Factory Method. The usage in our Application controller will look something like this:

    User user = Authenticator.getInstance().authenticate( username, password);

In this case Authenticator.getInstance() is the factory method. It doesn't necessarily create an Authenticator, but can create a subclass of Authenticator instead.

For design practice, let's define 2 Authenticator subclasses:

DatabaseAuthenticator - authenticate using username and password from database table.
ImapAuthenticator - authenticate using IMAP (such as KU Mail server)

EXERCISE:

  1. Draw a class diagram of this.
  2. We only need one Authenticator in the application. How would you ensure this?

QUESTION: Calling Authenticator.getInstance().authenticate(name,passwd) adds an extra layer of method calls, compared to User.authenticate(name,passwd). What GRASP Principle is this?

  1. Write the code for Authenticator and DatabaseAuthenticator. In Authenticator you can always create a Database authenticator (we'll fix that later):
public abstract class Authenticator {

    public static Authenticator getInstance() {
//TODO make this a singleton
        return new DatabaseAuthenticator();
    }

    /** authenticate user */
    public abstract User authenticate(String username, String password);
}
  1. This code always creates a DatabaseAuthenticator, which is not very flexible. How can you apply Protected Variations so you can easily change the type of concrete Authenticator to something else? One solution is to use a properties file to specify the name of the actual Authenticator class, and dynamically load this class. This is very common in Java applications. To change the app, you only need to change the properties file.

In Play, the conf/application.conf file is a properties file. You can add your own properties there. For safety, its a good idea to prefix your property names with something specific to your application. For example:

# this is conf/application.conf
exceed.authenticator=util.DatabaseAuthentictor

5. To read a string-valued property in Play, use:  
`String value = play.Play.application().configuration().getString("key name");`  
In this case:  
`String authclass =  play.Play.application().configuration().getString("exceed.authenticator");

Now you can dynamically load and create an instance of this class:
```java
Authenticator auth = (Authentictor) Class.forname( authclass ).newInstance( );

you should catch exceptions, log them, and then return some default authenticator.

  1. Test this by defining a NoPasswordAuthenticator that checks only the username, not the password.

Accessing Properties in Play

To access a configuration proerty value from your application conf/application.conf file, you can write:

String value = play.Play.application().configuration().getString("key name");

For example, if you define a property:

# in application.conf
exceed.authenticator=util.DatabaseAuthenticator

then you can write:

String authclass = play.Play.application().configuration().getString("exceed.authenticator");

Intro to Play Framework

ExceedVoting - assignment for 16-23 April.

Clone this wiki locally