Testing

The jots-testing module depends on the core module and provides ScalaCheck generators and instances, and String interpolators for secrets. Importantly, the testing module also allows us to create a VerifiedJwt from any SignedJwt for testing purposes. This means the module should generally only be used for testing purposes.

Getting Started

To get started with sbt, add the following line to your build.sbt file.

libraryDependencies += "se.vlovgr" %% "jots-testing" % "0.2.0" % Test

If you are using Scala.js or Scala Native, replace the %% with %%% above.

Supported Features

The ScalaCheck generators and instances can be made available with import jots.testing.*, while the String interpolators and syntax for unsafe verification requires import jots.testing.syntax.*. If you're looking for usage examples, the testing module is heavily used by the library tests.

ScalaCheck Support

The testing module provides ScalaCheck Gen generators and Arbitrary instances. Following are some samples from a few generators. Note private and public keys (PrivateKey and PublicKey) are not generated on the fly, but instead choose from a pre-defined list of keys. This is due to the generation being computationally expensive.

import cats.effect.IO
import jots.JwtBuilder
import jots.SignedJwt
import jots.VerifiedJwt
import jots.testing.*
import org.scalacheck.Arbitrary.arbitrary

// Generate an arbitrary token prior to signing
arbitrary[JwtBuilder].sample
// res0: Option[JwtBuilder] = Some(JwtBuilder(JwtHeader(alg -> "ES256",cty -> "軤䃾梙痻簁藺χᙨ僐苚☏Ҩꟲ㷔湋璡敁镂̊鉖஽Ἀ䁔焰ḯᡨ巴尷﹄ၴ↸薂嵚ᴜ쩿訋韄曽갛䫛ꃂ뿪䥡퍷⪷奄ﶳ툆ネ⧡㽴솀㤸넋⟣獻႟㗇恷ᷱ䤱㰡㚻篆ﲌ糭ෙ乁✖䡲詷ẜ敭閼ᚰ唬缅坥㑗秝ﮋ༒䚧辟癸帞棚",kid -> "ⱗ夨꿆謦쾚쁂䈿칋䣮ဘ濋공ⶖ벳㢰䯴셥ⶪ誆쥹썖ᰲ⊊䌬偖쇾ꗺ☺帼ா鸇ﻲ諄盶졜꠼짹춛ᠸ뼃͕쵇ꯂ",typ -> "幭⽇ꇐ㫎ཆ쿣ᥢ₰詫በ뺤ꫣꖛ陳궴붼ᛴ洋"),JwtClaims(iss -> "㛱䁋䲰듰묏 ฺ筵䶋⓸鳑犬蠦塾唼㼚취䇲닃袃纊䘅㳸銹㋫拾藪ѓ띠㾣㔜⢊읃贅წ恼獬䍩릿䜐켯᠚뻨闡䙡쎇Ο刎䒀쑤᥮롼ﶜ좑䭦㌳䑻톘ㄼ氶臑쉼鰔囙涩䆋䜜냺눙橩ꉶ餔寶賌︚쵦䡬“랼ⳡ",sub -> "鰉￸巻ᝠ풭ﶊ瘰埬嚪ⲥ줹刭୴抈艒꡹ो籦媔䧟摇烚뮆ﳚ헆봢늪뻽㋟愴篳贈ᑗㄌ娮捎㚛扉荿줟댝濱쵎鑃ɿ껗",aud -> "꥾⺍劙髧ժ퉇႑뾙얻뭳湨月ㅭ뺼贶꥞躺럀࢛≁Ͻ횔兆颭砑㰖᫺䁱ㄔㆪ㝼퇫邺塂퉣Ҳ諁﹙繈ﰻ붥ⓁЕ㉤粺⺵ʏ",exp -> -6594715421884647985,nbf -> 1,iat -> 9223372036854775807,jti -> "釷란離ܔ엜崝ş煨₦ⷍ魴ᓰ뼛ᅢ織ӎ붙⡾┮䩤ॽὛ釖㉣ꧠ䄉봰캔ྛ䢨᭙뀷푐䕝ࡽﭵꉚⱫ")))

// Generate an arbitary token with valid signature
arbitrary[SignedJwt].sample
// res1: Option[SignedJwt] = Some(SignedJwt(SignedJwtHeader(alg -> "HS384",cty -> "㫐㡑ⓞ앓渲꒨",kid -> "挃Ϯ읯䞋┷㈀챆脏貘䶨好퀢ꁙᴑ뱢ླྀ郟垀岢጖輒Ᾰᶛ䮾䪘小伖혡㞇掣࣋뽻㱓ొ鷞逸㘷䃄挛麛䚝ꎨ欢쪓",typ -> "ꝕ겹馔붶΀㯜㍢䴳"),SignedJwtClaims(iss -> "注膨얣髂⽌＀䐊嗀≽",sub -> "쬽滦➙젣멼㛲趱殨铓お愞쬁ࢽ쵏왵疞똲곞纟㽛浟繱ੜ⒥毝ꭏឤ폋뇠ⲩ查Ẹ┄ٕ䆢ᷨꐽ鞍폡쬘Ὓ룧즘퍦毁뗧훳갅칑砼㱶矛ే㤃캅梏倳먯",aud -> "⻚ꛧ䋭䕁䆚3⇔⊅巫㝬邓ᨺ트籿騧쾱鴤값⾺촑ဃ⣳甁樾ᵮዸ䐙鋛꧈㠣὿娰嶻橽扸ׁ숺ᏽ萸籺%짭寨菰픩\u001f张퉣Ể氣闐",exp -> -9223372036854775808,nbf -> 627082551353850939,iat -> -9223372036854775808,jti -> "铲㣒쟍埔Ԓꦴ䔰㞄몰")))

// Generate an arbitrary token with a verified signature
arbitrary[VerifiedJwt].sample
// res2: Option[VerifiedJwt] = Some(VerifiedJwt(SignedJwtHeader(alg -> "RS512",cty -> "␤ɕ䷡罃笓罤ᕯ",kid -> "",typ -> "JWT"),SignedJwtClaims(sub -> "⦪᳄黆寗掸⽜ﭣ注纪䄝缩됦㨧䢬琭砖ꅄ뙝矂籠ᵪ㦑꧆㼋醊㰠᭚库倐淭㬌翡俣췻爵度꟨ɢ鿉镐捫䭄鬳⇽쑨젂짬﹐៬ꦒ",aud -> "⸆㐊龷묐ⱔ⊖斱Ƭ㣶䊙䦄뀮퐰徂",exp -> -2211554968953121187,nbf -> 1,iat -> -1,jti -> "묛䥦䔮嵤異䋻≱ᆊ䙡ꡣ工⭁凄๾䲛嵆ⷌ䳺俍䄴붵鏵垵磤ἄꫦ㫥ⶍ尦墝덷ᮾ络渎ः쩿䳘ᗳﳌꥁ蒫㕼⁔꭫㔩넦넙撎밸鿵䄑殉즳鎽芆䶔\r値煉遖腿퐺瓫")))

// Generate an arbitary signing instance using ECDSA
ecdsaJwtSigningGen[IO].sample
// res3: Option[JwtSigning[[A >: Nothing <: Any] => IO[A]]] = Some(jots.JwtSigning$$anon$4@557d4d04)

// Generate a matching RSA private and public key
rsaKeyPairGen.sample
// res4: Option[Tuple2[PrivateKey, PublicKey]] = Some((PrivateKey(**),PublicKey(-----BEGIN PUBLIC KEY-----
// MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmTZoSk2SCHDBu5S1DjE9
// RDGw6YZwXZFJjG70gHfqjkuQF2DEqo48NWF4Syjgcl78y3pvgLiswqw6CgpoOyEs
// anRnvOSimxZxCNm8x/aRzOSIxq/iRpca9bLbGyv2TXeyBNLJOs6q8zPSwO9wYe3+
// +z+i0aemuenWN4iIBoLzB2lZXGwyn56T+s+HHsHRfCrXNrW0yYmKmhaefXfWrLtj
// kcXfUmMa5xYbysQXaFIUxiebBxmbDiaNw4DgYa2dutgZ1N7+FLpVcPAnCxGVabyJ
// bljpJoATC22Wq3h7fdd2q5AScBjMzxGnpqSqxwAx+4qqTQ32AjgTIMCvyzf+uc+j
// jG4qvZj5UqhRfc/Bedmh70l5kt69MZgzbtqjwOjJQmuqYb0m1Xj60xETxOZvSdG+
// fIeQrEPcwiWsRm8wxSVer31lLnenbYWZRj3FifAJ/wrz0eBLdto6cPVeXkDcKBu0
// eZQwiMoaaGsOMXu4Hdft+RzJC2HQe+vWLwzEPAbauPOcH1TRj/RbTuuaRJW7ZgxS
// 5xFzaNb4GDZdqyr0VGpePf00f258bPSk5FfZihqs1/D1dIEvfodmJO1UniA/XPpc
// vYZAnwKk/gEuHuYrDbtH88YnMSRFWWxQNNavrMrlqrCcUEU+jnqJip/9FwUIjjR/
// eEzqpgzqyimf1UJ9u/ldackCAwEAAQ==
// -----END PUBLIC KEY-----)))

String Interpolators

The jots-crypto module provides syntax for compile-time parsing of public keys (PublicKey). The testing module provides additional syntax for private keys (PrivateKey), secret keys (SecretKey), and tokens (SignedJwt). These are generally sensitive and should not be in source code, except when they are non-secret for testing purposes.

import jots.SignedJwt
import jots.crypto.PrivateKey
import jots.crypto.SecretKey
import jots.testing.syntax.*

val privateKey: PrivateKey =
  privateKey"""
    -----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
    OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
    1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
    -----END PRIVATE KEY-----
  """

val secretKey: SecretKey =
  secretKey"5BpYD67PafjVoefV11a06MVMGCmr1zoLrFGL019EEuoMtZszHqqpAd6frHFFgGXZ"

val signedJwt: SignedJwt =
  signedJwt"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1c2VySWQiOiI4ZDNiYmQxNC1kZmQ5LTQ3ZmEtYWFiNC1kNzZkYWYwMGI0ZjEiLCJleHAiOjMzNDUwNjI0MDAsImlhdCI6MTc2NzIyNTYwMH0.8i3xidY8bcAjoBYSKktcyihSdICGXBSBnjp13JYmO_DE5v4_oxY4bSBtZxdoic7OWFKZCcE63I1fFlukzgxVZA"

Unsafe Verification

It is possible to extend the default verifications for custom verifications. The core modules enforces that all instances of VerifiedJwt must have gone through signature verification. For testing purposes, the testing module allows us to create a VerifiedJwt from any SignedJwt without verification.

import jots.testing.syntax.*

signedJwt.toVerifiedUnsafe
// res5: VerifiedJwt = VerifiedJwt(SignedJwtHeader(typ -> "JWT",alg -> "ES256"),SignedJwtClaims(userId -> "8d3bbd14-dfd9-47fa-aab4-d76daf00b4f1",exp -> 3345062400,iat -> 1767225600))

VerifiedJwt.fromSignedUnsafe(signedJwt)
// res6: VerifiedJwt = VerifiedJwt(SignedJwtHeader(typ -> "JWT",alg -> "ES256"),SignedJwtClaims(userId -> "8d3bbd14-dfd9-47fa-aab4-d76daf00b4f1",exp -> 3345062400,iat -> 1767225600))