Creating and validating JWT JSON web tokens
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:
libraryDependencies ++= 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.
import java.time.Instant
import java.util.{Date, UUID}
import io.jsonwebtoken.{Claims, Jws, Jwts, SignatureAlgorithm}
import scala.collection.JavaConverters._
object Jwt {
val Ttl: Int = 3600
val Secret: String = "MySecret"
def apply(claims: Map[String, Any]): String = {
val jwt = Jwts.builder()
.setId(UUID.randomUUID.toString)
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plusSeconds(Ttl)))
.signWith(SignatureAlgorithm.HS512, Secret.getBytes("UTF-8"))
claims.foreach { case (name, value) =>
jwt.claim(name, value)
}
jwt.compact()
}
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.
def unapply(jwt: String): Option[Map[String, Any]] =
try {
val claims: Jws[Claims] = Jwts.parser()
.setSigningKey(Secret.getBytes("UTF-8"))
.parseClaimsJws(jwt) // we can trust this JWT
Option(claims.getBody.asScala.toMap)
} catch {
case _: Exception => None
}
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:
Option(claims.getBody.asScala.toMap)
Using our Jwt object
Create a token
val jwt = Jwt(Map(
"claim1" -> "value1",
"claim2" -> "value2",
"claim3" -> "value3"
))
Do not forget to encode your token if you are going to use it on urls:
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
URLEncoder.encode(jwt, StandardCharsets.UTF_8.toString)
Validate a token and get the claims.
jwt match {
case Jwt(claims) =>
val claim1 = claims("claim1").toString
val claim2 = claims("claim2").toString
val claim3 = claims("claim3").toString
// do something with your claims
case _ => // Invalid token
}
This implementation offers a flexible and easy way of creating and validating JWT’s.