Implementing Authentication on Play Framework

April 13, 2020 | 19 Minute Read

One of the most common things on any web application is authentication. Almost every web app needs authentication. If you want to have users then you’ll need to implement authentication. And there are many different ways to implement it today. From the classic user / password, to login with other social accounts (Google, Facebook, GitHub, etc), using JWT tokens, or even by using external services like auth0.

User authentication is a sensitive topic: everyone expects to work flawlessly, and if there’s any security issue it can easily take the full company down.

A small disclaimer: I’m by no means an expert on the subject, and I do not intend to make this blog post a tutorial on security and user authentication. If you are new to the subject, I recommend this web security essentials blog post series by Soham Kamani. It’s a very good crash course on web security and covers a wide range of topics such as:

Implementing User Authentication

So what is this article about? Well, if you understood the general idea, now it’s time to actually implement user authentication.

Several web frameworks provide very easy to use solutions which require very little work for the developer (sometimes a class annotation is all that takes to validate authentication). To be honest, I never had implemented user authentication from scratch using the Play Framework, so I was a bit curious to see how it could be achieved.

Let’s start by doing a very short summary on how authentication will work.

Quick overview on authentication

If you’ve read the Sessions and cookies tutorial listed before you can skip this section. If you didn’t, here are some important aspects of how authentication works:

Each request needs to be authenticated, i.e., you cannot assume that after a login request all the requests from the same address / device will be from the same user;

You don’t want to make the user type the username / email in every request;

To avoid that, after the login request you provide a secret (a session token) to the user / caller

The session token should be hard to guess;

The session token is mapped to a single user;

The user / client now uses that token in every request;

To make this automatic and invisible to the user, you add the token as a cookie;

The browser will include the cookie with the token in every request following the login;

Getting started

The first thing I did was look for existing libs / authentication mechanisms in Play Framework. Despite most frameworks do have plugins for authentication (for instance Spring Session), Play doesn’t offer anything like that. But to be fair, Play makes it very easy to implement.

I found a few open source solutions to handle authentication on Play Framework:

As usual, let’s start by creating a new Play Framework app from scratch by using the existing template:

$ sbt new playframework/play-scala-seed.g8

This should create a template app containing a HomeController.scala file which contains the controller of our tutorial.

Before heading into the juicy part of managing users and their requests we’ll need to add some extras to the current app:

A User representation (model)

A UserDAO which will fetch user information from DB

A SessionDAO which will store Session Tokens

For the sake of simplicity during this tutorial, we’ll replace persistent storage in DB with an in-memory Map.

Databases are obviously a must if you plan to run this in a real environment, but you should also take into account that these tables are going to be accessed a lot (in every request), so you should probably consider some sort of caching mechanism.

For User we create the following simple code:

packagemodelsimportscala.collection.mutablecaseclassUser(username:String,password:String)objectUserDAO{// Map username -> User
privatevalusers:mutable.Map[String, User]=mutable.Map()defgetUser(username:String):Option[User]={users.get(username)}// this method should be thread safe
defaddUser(username:String,password:String):Option[User]={// check if user already exists and return error if it does
if(users.contains(username)){Option.empty}else{valuser=User(username,password)users.put(username,user)Option(user)}}}

And for the session:

packagemodelsimportjava.time.LocalDateTimeimportjava.util.UUIDimportscala.collection.mutablecaseclassSession(token:String,username:String,expiration:LocalDateTime)objectSession{// Map token -> Session
privatevalsessions=mutable.Map.empty[String, Session]defgetSession(token:String):Option[Session]={sessions.get(token)}defgenerateToken(username:String):String={// we use UUID to make sure randomness and uniqueness on tokens
valtoken=s"$username-token-${UUID.randomUUID().toString}"sessions.put(token,Session(token,username,LocalDateTime.now().plusHours(6)))token}}

Now that we have our base built, let’s create a route for a private page. By default the template we used already adds a public homepage. Let’s add a route for the private page:

In the above snippet we can look at the implementation for a public page under index() method, and for a private one under priv() method. The private page starts by fetching the user from the request, by calling the extractUser(req: RequestHeader) method. This method contains the necessary logic to parse the request session.

As we can see, it’s fairly easy to access the session by calling val sessionTokenOpt = req.session.get(<whatever_session_key_we_define>). Play provides this utility method to access a special Cookie, meant to keep session tokens. We could just simply manage sessions as normal cookies by using request.cookies.get(<whatever_session_key_we_define>), but this helper give us some nice advantages. For instance, session cookies are stored in JWT format, meaning they are signed. You can read more on the advantages of using the Play Sessions instead of normal Cookies and how to configure session on the official Play documentation.

The next interesting part is to see that given a request session, we now go into the SessionDAO to get the full session info. We then check if it has already expired, and if doesn’t, then we get the corresponding session User. And that’s it, we have the User which made the request and that can be used to get whatever private data you need.

But maybe you noticed as we would need to add to every private route the very same first line:

valuserOpt=extractUser(request)

We can further leverage the power of scala by using Higher Order Functions and implicit parameters and create a much better flow. Let’s see how:

defpriv()=Action{implicitrequest:Request[AnyContent]=>withUser(user=>Ok(views.html.priv(user)))}privatedefwithUser[T](block:User=>Result)(implicitrequest:Request[AnyContent]):Result={valuser=extractUser(request)user.map(block).getOrElse(Unauthorized(views.html.defaultpages.unauthorized()))// 401, but 404 could be better from a security point of view
}

And now sounds a lot cleaner!

Play helpers

But we can reduce our code even further by using Play Security object which provides utility methods for doing what we did already:

While it’s not a big difference, delegating this important functionality to the (already tested) framework gives you some confidence the code works fine. And if there’s any security bug found, you can simply update the framework version to get the fix for free.

Using Play custom Actions

There’s still another improvement we can make. We can add this logic to a custom Action, which is what the documentation recommends.

This requires a bit more understanding of the framework, but it’s still fairly easy and can be accomplished with a few lines of code. First we need to create our new custom Action:

First note that we need to inject our new custom action on the controller class constructor. After that, its usage is very straightforward. Just like we did for our public page, but instead of creating an Action, we create our new UserAction, and we have instant access to the request user.

And finally, how can we test this? Well, are missing users and the login right?

Login

Creating users has little importance on this session management tutorial so we won’t go into much detail. We’d just need to have a page with a registration form, receive the data on the server side, and creating a new user on our database. If you are not sure on how to do it you can quickly see how to create a form on Creating forms on your Play application and how to store data persistently on Play Framework and Slick example.

The login is a bit more interesting. It’s on the login where session management starts.

First we need a login endpoint. This is a simple endpoint that receives an username and password. This endpoint should always be a POST endpoint and the user credentials should be sent in the POST body. The POST data is encrypted. If you use a GET, you’ll be exposing sensitive data to attackers. Thus said, we’ll be using a GET just for the sake of this example

GET /login controllers.HomeController.login(username, password)

And the corresponding controller method:

deflogin(username:String,pass:String)=Action{implicitrequest:Request[AnyContent]=>if(isValidLogin(username,pass)){valtoken=SessionDAO.generateToken(username)Redirect(routes.HomeController.index()).withSession(request.session+("sessionToken"->token))}else{// we should redirect to login page
Unauthorized(views.html.defaultpages.unauthorized()).withNewSession}}privatedefisValidLogin(username:String,password:String):Boolean={UserDAO.getUser(username).exists(_.password==password)}

So the logic here is to first check if the given login is valid. Since we are storing passwords in plain text this is easy. Otherwise we’d need to hash the received password to check if they matched (See how to storage passwords securely).

If the login is valid, we now create a new session token and respond to the request with a new session token stored in the session Cookie.

Don’t forget that the token generation should use a random factor. This makes it hard for attackers to guess and helps avoiding duplicated tokens.

Working example

If you had any problems following the tutorial, make sure you look at the working example repository. Just follow the README file to run in your local environment, and play with it!

Leave a comment here or in the repository github page if you are stuck somehow.

Conclusion

Authentication is a fundamental functionally for any web application. Each framework provides its own set of tools to make a developer life easier. In case of our Play Framework, it requires a minimal amount of work to implement any of the several alternative solutions we covered.

But it’s not over yet. Now that you have users, you’ll probably need to implement user permissions. Maybe you want admin users to access some special settings. Maybe you have a kind of users that play a Manager role. Or maybe you want to provide a more fined grained authorization mechanism. Authorization it’s another different beast, a bit more complex than authentication. Nevertheless, don’t be afraid. Start with a simple mechanism, and make it extensible. What may work for your initial application won’t most likely work in the long term as you get more users and more features.