philippus / baggage-jwt

🛄 JSON Web Tokens / JWT (RFC 7519) in Scala

GitHub

Baggage JWT

Build Status codecov Current Version License

Baggage JWT is an implementation of RFC 7519 in Scala that tries to follow the specification closely.

Installation

Baggage JWT is published for Scala 2.12. Add the following to your build.sbt:

resolvers += Resolver.bintrayRepo("gn0s1s", "releases")

libraryDependencies += "nl.gn0s1s" %% "baggage-jwt" % "0.2.0"

JWT

Example usage

Creating a token

A JsonWebToken can be created through three methods:

  • JsonWebToken(header, claims, secretKey) with the parameters:

    • header: JoseHeader - the header to use
    • claims: ClaimsSet - the claims to encode in the jwt
    • secretKey: Key - the key used to sign the jwt
  • JsonWebToken(alg, claims, secretKey): in this case a default header will be generated for the supplied algorithm alg

  • JsonWebToken(jwtString): here the jwtString is an already encoded JWT.

import nl.gn0s1s.baggage._
import nl.gn0s1s.baggage.algorithm._
import nl.gn0s1s.baggage.claim._
JsonWebToken(header = JoseHeader(HS256, Some("JWT"), None), Set(SubjectClaim("1234567890"), PublicClaim("name", "John Doe"), PrivateClaim("admin", true)), Key("secret".getBytes))
// res0: scala.util.Try[nl.gn0s1s.baggage.JsonWebToken] = Success(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ)

JsonWebToken(alg = HS256, Set(SubjectClaim("1234567890"), PublicClaim("name", "John Doe"), PrivateClaim("admin", true)), Key("secret".getBytes))
// res1: scala.util.Try[nl.gn0s1s.baggage.JsonWebToken] = Success(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ)

JsonWebToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ")
// res2: scala.util.Try[nl.gn0s1s.baggage.JsonWebToken] = Success(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ)

Header

A header can be generated using JoseHeader(alg, typ, cty) with the parameters:

  • alg: algorithm header parameter to use in the jwt
  • typ: type header parameter, usually contains "JWT"
  • cty: content type header parameter, usually can stay empty

An extra method JoseHeader(alg) is available where only the alg parameter needs to be provided, a typ of "JWT" and no cty will be added as defaults.

JoseHeader(HS256, Some("JWT"), None)
// res3: nl.gn0s1s.baggage.JoseHeader = JoseHeader(HS256,Some(JWT),None)
JoseHeader(NoneAlgorithm)
// res4: nl.gn0s1s.baggage.JoseHeader = JoseHeader(none,Some(JWT),None)

Claims

For the registered claim names: iss, sub, aud, exp, nbf, iat, and jti there are implementations available. Baggage JWT is strict about the allowed values inside registered claims.

Algorithms

The following algorithms are supported:

  • HS256
  • HS384
  • HS512
  • None

Keys

Validating a token

To validate a token the method JsonWebToken.validate can be used, it requires:

  • jwtString: the jwt being validated (as a string)
  • alg: the algorithm to validate against
  • secretKey: the key to validate against
val jwt = res0.get
JsonWebToken.validate(jwt.toString, HS256, Key("secret".getBytes))
// res5: Boolean = true

Processing claims

Most likely you want to write your own claims processor, but an example of a claims processor (see: ClaimsProcessor.process) is included which requires the following parameters:

  • claims: the claims being processed.
  • reqClaimNames: the names of claims that are required to be there.
  • expectedClaims: the claims that are expected to match exactly, note that the aud claim is processed differently than the rest.
  • clockSkew: the clock skew to take into account for the exp and nbf claims.

The result is a Success containing the claims, or a Failure containing an appropriate exception.

val claims = jwt.decode.get._2
ClaimsProcessor.process(claims, Set("sub", "name", "admin"), Set(PrivateClaim("admin", true)), java.time.Duration.ZERO)
// res6: scala.util.Try[nl.gn0s1s.baggage.claim.ClaimsSet.ClaimsSet] = Success(Set(SubjectClaim(1234567890), PublicClaim(name,John Doe), PrivateClaim(admin,true)))
ClaimsProcessor.process(claims, Set("sub", "name", "admin", "iat"), Set(PrivateClaim("admin", true)), java.time.Duration.ZERO)
// res7: scala.util.Try[nl.gn0s1s.baggage.claim.ClaimsSet.ClaimsSet] = Failure(java.lang.IllegalArgumentException: Not all required claim names present in claims set)

References

License

The code is available under the BSD 3-Clause.