Integrate JWT with Akka HTTP

Knoldus Inc.
Knoldus - Technical Insights
3 min readJun 18, 2016

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 :

[code language=”json”]
{
“alg”: “HS256”,
“typ”: “JWT”
}
[/code]

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 :

[code language=”json”]
{
“iss”: “narayan”,
“aud”: “admin”
}
[/code]

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.

[code language=”scala”]
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”
)
[/code]

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

[code language=”scala”]
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
}
}
[/code]

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 :

[code language=”scala”]
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”)
}
}
}
}
}
}
[/code]

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 !!!

--

--

Knoldus Inc.
Knoldus - Technical Insights

Group of smart Engineers with a Product mindset who partner with your business to drive competitive advantage | www.knoldus.com