avatar

Andres Jaimes

Creating and validating JWT JSON web tokens

By Andres Jaimes

- 2 minutes read - 381 words

This article goes through the process of creating and validating JWT’s (JSON web tokens) using Scala.

Our implementation uses the awesome io.jsonwebtoken library, and can be added to a sbt project like this:

1libraryDependencies ++= Seq("io.jsonwebtoken" % "jjwt" % "0.9.1")

Creating a token

We are going to use Scala’s apply/unapply functions for this implementation. This will allow us to use matchers for checking JWT’s.

 1import java.time.Instant
 2import java.util.{Date, UUID}
 3
 4import io.jsonwebtoken.{Claims, Jws, Jwts, SignatureAlgorithm}
 5
 6import scala.collection.JavaConverters._
 7
 8object Jwt {
 9
10  val Ttl: Int = 3600
11  val Secret: String = "MySecret"
12
13  def apply(claims: Map[String, Any]): String = {
14    val jwt = Jwts.builder()
15      .setId(UUID.randomUUID.toString)
16      .setIssuedAt(Date.from(Instant.now()))
17      .setExpiration(Date.from(Instant.now().plusSeconds(Ttl)))
18      .signWith(SignatureAlgorithm.HS512, Secret.getBytes("UTF-8"))
19    
20    claims.foreach { case (name, value) =>
21      jwt.claim(name, value)
22    }
23    
24    jwt.compact()
25  }

Ideally Ttl and Secret should be provided by a configuration object. Such an object can be passed as an implicit parameter to this function.

Use a long secret string. It’s not unusual to have secrets of more than 340 characters.

Validating a token

Add the unapply function.

1  def unapply(jwt: String): Option[Map[String, Any]] =
2    try {
3      val claims: Jws[Claims] = Jwts.parser()
4        .setSigningKey(Secret.getBytes("UTF-8"))
5        .parseClaimsJws(jwt) // we can trust this JWT
6      Option(claims.getBody.asScala.toMap)
7    } catch {
8      case _: Exception => None
9    }

Similar to its apply counterpart, this function may receive an implicit configuration object that holds the Secret string value.

The parseClaimsJws function will throw an exception if the passed token has expired or has been tampered. Therefore, it is safe to read the accompanying claims using:

1    Option(claims.getBody.asScala.toMap)

Using our Jwt object

Create a token

1val jwt = Jwt(Map(
2          "claim1" -> "value1",
3          "claim2" -> "value2",
4          "claim3" -> "value3"
5        ))

Do not forget to encode your token if you are going to use it on urls:

1import java.net.URLEncoder
2import java.nio.charset.StandardCharsets
3
4URLEncoder.encode(jwt, StandardCharsets.UTF_8.toString)

Validate a token and get the claims.

1jwt match {
2  case Jwt(claims) =>
3    val claim1 = claims("claim1").toString
4    val claim2 = claims("claim2").toString
5    val claim3 = claims("claim3").toString
6    // do something with your claims
7  case _ => // Invalid token
8}

This implementation offers a flexible and easy way of creating and validating JWT’s.

References