JWT Authentication with Play Framework

Table of contents
Reading Time: 3 minutes
In this blog, I will demonstrate how to implement JWT Authentication with Play Framework.

JSON Web Token (JWT) is a compact, self-contained which means securely transfer the information between two parties. It can be sent via Post request or inside the HTTP header. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret or a public/private key pair using RSA It consists three parts separated by the dot(.)  i.e Header, Payload and Signature. The header consists type of the token and hashing algorithm, the payload contains the claims and the claims in a JWT are encoded as a JSON object that is used as the payload or as the plaintext, the signature part is used to verify that the sender of the JWT.

Here, we are using JSON Web Token for authentication with Play Framework, This is a most common way of using JWT. Once the user is logged in, we need to include JWT with each request that allows the user to access the routes, service and resources which authenticate the token in order to permit the request.

Let’s start with the implementation of JWT Authentication using Play Framework:

1. First, we need to include JWT library in our project. There are several libraries for JWT, you can use any of them depending on your requirement, here I am using one of the library i.e.

"com.jason-goodwin" % "authentikat-jwt" % "0.4.5"

2. Next, you need to create JWT utility class in which you need to add all these methods for creating, verifying and decode the JWT token and provide the secret key and algorithm such as HMAC, SHA256.

val JwtSecretKey = "secretKey"
val JwtSecretAlgo = "HS256"

def createToken(payload: String): String = {
  val header = JwtHeader(JwtSecretAlgo)
  val claimsSet = JwtClaimsSet(payload)

  JsonWebToken(header, claimsSet, JwtSecretKey)
}

def isValidToken(jwtToken: String): Boolean =
  JsonWebToken.validate(jwtToken, JwtSecretKey)

def decodePayload(jwtToken: String): Option[String] =
  jwtToken match {
    case JsonWebToken(header, claimsSet, signature) => Option(claimsSet.asJsonString)
    case _                                          => None
  }

3. To implement JWT authentication, we need to create reusable custom secured action by using ActionBulder that authenticates the each subsequent request and verifies the JWT in order to permit the request to access the corresponding service. ActionBulder is the special case of functions that take request as input and thus can build actions and provides several factory methods that help for creating action. To implement ActionBuilder, we need to implement invokeBlock method. So here I have created a custom JWTAuthentication by using ActionBuilder.

case class UserInfo(id: Int,
                    firstName: String,
                    lastName: String,
                    email: String)

case class User(email: String, userId: String)

case class UserRequest[A](userInfo: UserInfo, request: Request[A]) extends WrappedRequest(request)

class SecuredAuthenticator @Inject()(dataSource: DataSource) extends Controller {
  implicit val formatUserDetails = Json.format[User]

  object JWTAuthentication extends ActionBuilder[UserRequest] {
    def invokeBlock[A](request: Request[A], block: (UserRequest[A]) => Future[Result]): Future[Result] = {
      val jwtToken = request.headers.get("jw_token").getOrElse("")

      if (JwtUtility.isValidToken(jwtToken)) {
        JwtUtility.decodePayload(jwtToken).fold {
          Future.successful(Unauthorized("Invalid credential"))
        } { payload =>
          val userCredentials = Json.parse(payload).validate[User].get

          // Replace this block with data source
          val maybeUserInfo = dataSource.getUser(userCredentials.email, userCredentials.userId)

          maybeUserInfo.fold(Future.successful(Unauthorized("Invalid credential")))(userInfo => block(UserRequest(userInfo, request)))
        }
      } else {
        Future.successful(Unauthorized("Invalid credential"))
      }
    }
  }

}

4. Now we can use “JWTAuthentication” the same way we use Action.

  def index = auth.JWTAuthentication { implicit request =>
    Ok(views.html.index(s"Hello ${request.userInfo.firstName} ${request.userInfo.lastName}"))
  }

5.  You can test it through Postman(on which you can send the request and view response),  all we need to do is create a JWT and pass it to the Headers, corresponding to the field “jw_token”. You can create JSON web token by “createToken” method that I have shown you in step 2 by passing payload as a parameter which can be anything, here is the example: 

val payload = """{"email":"test@example.com","userId":"userId123"}"""

JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20iLCJ1c2VySWQiOiJ1c2VySWQxMjMifQ.mjMQN8m_wH1NSE9GGexCW_GUh8uruNco18jgt7AWuO4

jwt.png

You can get the source code from here.

I hope this blog is helpful to you!

Thanks 

References:
 

KNOLDUS-advt-sticker

Written by 

Teena is a Senior Software Consultant at Knoldus Software LLP having more than 3 year experience working in Scala, Akka, Play Framework. She is very enthusiastic towards her work and good at working in team. She is very much keen to learn new technologies. She is familiar with programming languages such as Scala, Play Framework, Akka, Javascript, HTML, Java.

5 thoughts on “JWT Authentication with Play Framework3 min read

  1. Your ActionBuilder actually doesn’t call the nested block passed to the invokeBlock method (seems a bit of a joke =D). It does auth and returns an Ok to the caller.
    In the index example, the index page would never be rendered. I checked the codebase and I see the same situation.

    1. Hi Ivano Pagano, every request on this action added to the example invokes the invokeBlock method and hence the authentication process. Based on the JWT value passed to request header this invokeBlock decides whether to authenticate the request or not. If you’ll use the JWT added in this blog post the action would return an Ok response as the JWT contains valid email and userId information, but if you’ll use any other random token the request would not be authenticated. To exemplify this I’ve updated the code base a bit and it should be more clear now. You can try it using curl request or postman (chrome plugin). Thanks for the heads up. Hope it helps 🙂

  2. Hi, 0.4.1 is dangerous, some people can just copy past your example, so please, change the `”com.jason-goodwin” %% “authentikat-jwt” % “0.4.1”` to the `”com.jason-goodwin” %% “authentikat-jwt” % “0.4.5”` to protect your readers.

    From the github repo of the project: “Please update to 0.4.5 as soon as possible. Previous versions may be vulnerable to injection in cases where user controls values in the claims.”

  3. Hi,
    BTW nice write up.
    As quoting to your lines…
    >> // Replace this block with data source
    >> val maybeUserInfo = dataSource.getUser(userCredentials.email, userCredentials.userId)
    I have a doubt what is the use of JWT then if we always keep checking the dataSource for every request ?

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading