Integrate JWT with Akka HTTP


In this article we will discuss about , how to implement  authentication or authorization in Akka HTTP routes using JWT .

As we know Akka HTTP is full implementation of  server  and client side HTTP stack on top Akka actor and Akka stream . Now Let’s  we  talk about JWT.

what is JWT ?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

What is the JSON Web Token structure?

JSON Web Tokens consist of three parts separated by dots “.”, which are:

  • Header
  • Payload
  • Signature

Let’s dive into  detail of each part.

Header :  The header typically consists of two parts: the type of the token, which is JWT, and the hashing algorithm being used, such as HMAC SHA256 or RSA.

For Example :

{
"alg": "HS256",
"typ": "JWT"
}

Payload: The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional metadata.

For Example :

{
"iss": "narayan",
"aud": "admin"
}

Signature : To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

So Let’s start with code , you have to add following dependencies in your build.sbt file.

Seq(
"com.typesafe.akka" %% "akka-stream" % "2.4.7",
"com.typesafe.akka" %% "akka-http-core" % "2.4.7",
"com.typesafe.akka" %% "akka-http-experimental" % "2.4.7",
"io.igl" %% "jwt" % "1.2.0",
"mysql" % "mysql-connector-java" % "5.1.34",
  "com.typesafe.akka" %% "akka-http-testkit" % "2.4.3",
  "org.scalatest" %% "scalatest" % "2.2.6" % "test"
)

Now , we have to create an Authentication Handler to  handle JWT related operations .

trait AuthenticationHandler {

  def isVerifyWithRole(req: HttpRequest, secretKey: String, role: String): Boolean = {
    val result = getAuthToken(req)
    val res: Try[Jwt] = DecodedJwt.validateEncodedJwt(
      result, // An encoded jwt as a string
      secretKey, // The key to validate the signature against
      Algorithm.HS256, // The algorithm we require
      Set(Typ), // The set of headers we require (excluding alg)
      Set(Iss, Aud),
      iss = Some(Iss("narayan")), // The iss claim to require (similar optional arguments exist for all registered claims)
      aud = Some(Aud(role))
    )
    res.isSuccess
  }

  def createTokenWithRole(secretKey: String, userName: String, role: String): String = {
    val jwt = new DecodedJwt(Seq(Alg(Algorithm.HS256), Typ("JWT")), Seq(Iss(userName), Aud(role)))
    jwt.encodedAndSigned(secretKey)
  }

  def getAuthToken(req: HttpRequest): String = {
    val AUTHORIZATION_KEYS: List[String] = List("Authorization", "HTTP_AUTHORIZATION", "X-HTTP_AUTHORIZATION", "X_HTTP_AUTHORIZATION")
    def authorizationKey: Option[String] = AUTHORIZATION_KEYS.find(req.getHeader(_) != null)
    val result = if (authorizationKey.isDefined && authorizationKey.get == "Authorization") {
      req.getHeader("Authorization").get().value()
    } else {
      "request have not authorize token"
    }
    result
  }
}

This AuthHandler has three methods like createTokenWithRole()  which creates JWT token with the help of secret key , username and role, second is getAuthToken() which fetch JWT token from HTTP Request and last method is isVerifyWithRole() which is use to verify incoming HTTP Request with the help of secret key and user role.

Now , we have to create Akka HTTP routes with JWT AuthenticationHandler :

trait JwtApi extends JwtApiHandler with AuthenticationHandler {
  implicit val system: ActorSystem
  implicit val materializer: ActorMaterializer
  val secretKey = "secret"
  val routes: Route =
    get {
        path("getAllUsersName" / "admin") { ctx: RequestContext =>
          val r: String = if (isVerifyWithRole(ctx.request, secretKey, "admin")) {
            val allUsersName: ListBuffer[String] = getAllUserName()
            s"user names are  ${allUsersName.toString()} "
          } else {
            "other users......"
          }
          ctx.complete(r)
        } 
    } ~
      post {
        path("login") {
          entity(as[Multipart.FormData]) { data =>
            complete {
              val extractedData: Future[Map[String, Any]] = data.parts.mapAsync[(String, Any)](1) {
                case data: BodyPart => data.toStrict(2.seconds).map(strict => data.name -> strict.entity.data.utf8String)
              }.runFold(Map.empty[String, Any])((map, tuple) => map + tuple)
              extractedData.map { data =>
                val userName = data.get("username").get.toString
                val password = data.get("password").get.toString
                val result = getLoginInfo(userName)
                val res = if (result._1 == userName && result._2 == password) {
                  val encodedToken = if (result._3 == "admin") {
                    createTokenWithRole(secretKey, userName, result._3)
                  } else {
                    createToken(secretKey, userName)
                  }
                  HttpResponse(StatusCodes.OK, entity = s"Data : $encodedToken successfully saved.")
                } else {
                  HttpResponse(StatusCodes.Unauthorized, entity = "login credentials are invalid")
                }
                res
              }
                .recover {
                  case ex: Exception => HttpResponse(StatusCodes.InternalServerError, entity = "Error in processing the multi part data")
                }
            }
          }
        }
      }
}

I have added test cases of these routes , so that you can easily test it.

I hope, it will be helpful for you.

You can find source code here

Happy Blogging !!!

About Narayan Kumar

Software Consultant at Knoldus Software LLP.
This entry was posted in Akka, akka-http, integration, Integration testing in play framework, JWT, sbt, Scala, scalatest, Security. Bookmark the permalink.

3 Responses to Integrate JWT with Akka HTTP

  1. adamw1pl says:

    You can also take a look at https://github.com/softwaremill/akka-http-session, which has a JWT module

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s