Basic Authentication in the Play Framework Using A Custom Action Annotation

How to protect controllers in the Play Framework with basic authentication using a custom action annotation

Published: 07 Jun 2012

If you need a simple way to protect certain controllers in your Play Framework app with basic authentication, one option is using a custom action annotation. This post was also inspired by a couple of great posts about how to write modules for Play written by Steve Chaloner and found here.

Let's get started. First, create a new package app/actions. Then add an annotation app/actions/BasicAuth with the following:

package actions;

import play.mvc.With;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@With(BasicAuthAction.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
@Documented
public @interface BasicAuth {
}

Note the @With which will reference a new class that will intercept any controller or controller method annotated with the new @BasicAuth annotation. Create the app/actions/BasicAuthAction.java class with something like the following to actually take care of the basic authentication bits:

package actions;

import models.User;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;

public class BasicAuthAction extends Action {

    private static final String AUTHORIZATION = "authorization";
    private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
    private static final String REALM = "Basic realm=\"Your Realm Here\"";

    @Override
    public Result call(Http.Context context) throws Throwable {

        String authHeader = context.request().getHeader(AUTHORIZATION);
        if (authHeader == null) {
            context.response().setHeader(WWW_AUTHENTICATE, REALM);
            return unauthorized();
        }

        String auth = authHeader.substring(6);
        byte[] decodedAuth = new sun.misc.BASE64Decoder().decodeBuffer(auth);
        String[] credString = new String(decodedAuth, "UTF-8").split(":");

        if (credString == null || credString.length != 2) {
            return unauthorized();
        }

        String username = credString[0];
        String password = credString[1];
        User authUser = User.authenticate(username, password);

        return (authUser == null) ? unauthorized() : delegate.call(context);
    }
}

For completeness, the User.authenticate method called above might look something like:

public static User authenticate(String username, String password) {
  return find.where().eq("username", username).eq("password", password).findUnique();
}

Now you can use the new @BasicAuth annotation on your controller classes or individual methods to protect them with basic auth.

If you need something more robust than basic auth such as OAuth, OAuth2 or OpenID authentication, I recommend the SecureSocial module.


Play! Framework Jobs

jobs by Indeed job search