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:
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.