{"id":23477799,"url":"https://github.com/michael-yuji/pure-jwt","last_synced_at":"2025-04-13T14:17:06.933Z","repository":{"id":171734696,"uuid":"472084470","full_name":"michael-yuji/pure-jwt","owner":"michael-yuji","description":"Pure, functional JWT library for Scala, works with any Monad, any Json library","archived":false,"fork":false,"pushed_at":"2022-03-20T19:43:37.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T14:17:04.345Z","etag":null,"topics":["functional-programming","jwt","jwt-tokens","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michael-yuji.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-20T19:40:50.000Z","updated_at":"2022-03-26T13:16:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0dea124-e3b8-475f-873e-d804347265fe","html_url":"https://github.com/michael-yuji/pure-jwt","commit_stats":null,"previous_names":["michael-yuji/pure-jwt"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-yuji%2Fpure-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-yuji%2Fpure-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-yuji%2Fpure-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-yuji%2Fpure-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michael-yuji","download_url":"https://codeload.github.com/michael-yuji/pure-jwt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248724625,"owners_count":21151561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","jwt","jwt-tokens","scala"],"created_at":"2024-12-24T18:34:01.068Z","updated_at":"2025-04-13T14:17:06.908Z","avatar_url":"https://github.com/michael-yuji.png","language":"Scala","readme":"# Pure-JWT\n\nPure-jwt is a pure and functional package for issue/validate/decode JWT.\nThis package focus primarily focus on portability and observability, \nhence no explicit Json library dependencies at all. In fact, the only\ndependency is `cats`, as `cats` implements the Monad typeclass on various\nscala types and has great interoperability with all effect libraries and their\nmonads.\n\nYou can easily use any Json library you like / already using. You can easily \nadd support to any Json libraries by implementing the `JsonDriver` typeclass. \n`circe` support is available as a separate module and is a great example (only \n17 lines with imports!) \n\n# Introduction\n\nThe `core` package provides a monadic framework to work with JWT (Json Web Token),\ntherefore you can use any of your favoured monad / effect, let it be `ZIO` or \n`cats-effect` or `monix`  as long as they can interop with `cats`.\n\nThis package also provides a `EmptyM` monad which basically just a trivial monad.\nIf you are not comfortable coding with Monad and expect to write synchronized code,\nthe `EmptyM` can provide you a much friendly interface to work with. Check scalatest\ncode for examples.\n\n\nThe `core` package along does not implement the signing algorithms directly. \nYou most likely also need the `jdkcrypto` to support various algorithms. \nSupported algorithms are currently:\n\n- `HS256`, `HS384`, `HS512`\n- `RS256`, `RS384`, `RS512`\n- `ES256`, `ES384`, `ES512`\n\nThis package introduces the concept of validator and issuer. Which will discussed\nin the following chapters.\n\n# Token validation\n\nThis session documents how to validate token and build your own validation pipeline,\nif you only interested in using this framework directly, check the botom of this\nsession, or better, check the scalatest code.\n\nA validator is basically a trait that contains a function that to determine if a \ntuple of `(jwt_headers, jwt_claims, signature)` is valid. Since the library is \ndecided to be Monad aware, the type signature of such trait and function is\n```scala\ntrait JwtValidator[M[_], J] { self =\u003e\n  def validateM[M[_], J](headers: J, claims: J, token: String): M[ValidationResult]\n  ... \n}\n```\n\nwhere `M` is a Monad (for example `Task` in `zio` or `Future` in scala), `J` is \nthe Json type you expecting (for example `io.circe.Json`). \n\n`ValidationResult` is a builtin `sealed trait` that defines the following cases.\nNotice that for `DecodeFailure` and `InvalidToken`, developer can store any \ninformation about on the error.\n```scala\nsealed trait ValidationResult\ncase object ValidToken                 extends ValidationResult\ncase object SignatureNotMatch          extends ValidationResult\ncase object NotToken                   extends ValidationResult\ncase class  DecodeFailure[T](value: T) extends ValidationResult\ncase class  InvalidToken[T](value: T)  extends ValidationResult\n```\n\nBy nature, `Validator` are also chainable, the following methods are provided\nto build your custom validation chain:\n```scala\ntrait JwtValidator[M[_], J] { self =\u003e\n  /* returns a new validator that returns ValidToken if all validators returns valid */\n  def all(validators: JwtValidator[M, J]*): JwtValidator[M, J]\n  /* returns a new validator that returns ValidToken if any validators returns valid */\n  def any(validators: JwtValidator[M, J]*): JwtValidator[M, J]\n  /* you get the idea */\n  def and(validator: JwtValidator[M, J]):   JwtValidator[M, J]\n  def or(validator: JwtValidator[M, J]):    JwtValidator[M, J]\n}\n```\n\nAfter a validator is created, call the `validate(token)` method\n```scala\ndef validate(token: String)(implicit ev: Monad[M], driver: JsonDriver[J]): F[ValidationResult] \n```\n\n### Builtin validators\nThe `core` package defines a few validators to validate common claims such as\n`exp`, `iss`, `nbf`.\n\nThe `jdkcrypto` package defines a few jwt algorithms. For example the `hs256`\ninstance provide an instance can you can create a validator from, that \n**only validates the token signature**.\n```scala\nimport cats._\nimport jwt.jdkcrypto._\nimport scala.concurrent.Future\n/* hs256 is from the package jdkcrypto\n * We use `Future` as the example monad, and `Json` for the example Json type\n * this validator (and any validator from $arg.validator) only validates signature\n */\nval signatureValidator = hs256.validator[Future, Json](\"your-token\")\n\n/* create a validator that check token expiration and signature\n *   The argument validateExp function expects an integer value eqauls to `time now`\n *   this means validateExp does not rely on system clock at all!\n */\nval checkSignatureAndExp = signatureValidator.and(validateExp(1647000000 /* see comment above !! */))\n```\n\n# Decoder\n\nSome times, you don't only want to validate if a token is valid, but you want to\nextract information from the token. Given any `validator`, you can create a decoder\nby using the `decoder` method. a `JwtDecoder[M, J, A]` is an object that's capable\nof convert a `JwtWebToken[J]` to a type `A` wrapped in a monad `M`, and using `J` as \nthe underlying Json type.\n\nServeral methods, such as `trymap`, `mapEither` are provided such that it is \neasier to build the decoder easier. \n```scala\nfinal case class JsonWebToken[J](header: J, claims: J, token: String)\n\ndef decoder(implicit ev: Monad[M]) : JwtDecoder[M, J, JsonWebToken[J]]\n\nval mydecoder = my_validator.decoder\n```\n\nFor example if you want to deserialize the `claims` to `Foo`, with a `Decoder[Foo]`\nfrom `circe`. If you haven't used `circe` before, `decodeJson(...)` returns \na `Either[io.circe.DecodingFailure, Foo]` type. If the decoding failed\nsuch as `decodeJson` returns a `Left` value, the `Left` value will wrap in\n`DecodeFailure` of `ValidationResult`. If there are any validation failure,\nthe `Left` value will set to the error that fails the routine.\n\n```scala\nval fooDecoder: io.circe.Decoder[Foo] = ???\nval mydecoder = my_validator.decoder.mapEither(token =\u003e fooDecoder.decodeJson(token.claims))\n\nmydecoder.validate(my_jwt_token) // M[Either[ValidationResult, Foo]]\n```\n\n# Issuing Token\n\nIssue token is relatively simple. This part however maybe changed in the future \nrelease.\n\nA `JwtIssuer` is an instance that can sign a `JWT` token, you only need to provide\nthe list of claims (a list of `(String, Json)` tuple) that you want to add to the \nclaims body, call `signClaims` will be enough to issue a token. The example below \nuses `circe` as `Json`, and `EmptyM` as the monad\n```scala\nval issuer = hs256.issuer[EmptyM, Json](\"your-256-bit-secret\")\nval token: EmptyM[String] = issuer.signClaims((\"iss\" -\u003e Json.fromString(\"test\")), (\"sub\" -\u003e Json.fromString(\"1234567890\")))\n```\n\nThe monad interface for token signing may look overkill as serializing json isn't\nusually something you have to put in a effect and run. However, since an issuer\ncan literally be anything, as long as can generate a signature, it is possible\nfor an issuer need a monadic interface, for example calling `openssl` in shell,\nor even sending the unsigned token to an upstream server to sign it.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-yuji%2Fpure-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichael-yuji%2Fpure-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-yuji%2Fpure-jwt/lists"}