Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs

Last update: Jul 21, 2022

kJWT

Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs.

The following Algorithms are supported:

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • ES256 (secp256r1 curve)
  • ES256K (secp256k1 curve)
  • ES384
  • ES512

Usage

Include the following dependency: io.github.nefilim.kjwt:kjwt:0.1.5 in your build.

Creating a JWT

val jwt = JWT.es256("kid-123") {
    subject("1234567890")
    issuer("nefilim")
    claim("name", "John Doe")
    claim("admin", true)
    issuedAt(LocalDateTime.ofInstant(Instant.ofEpochSecond(1516239022), ZoneId.of("UTC")))
}

will create the following:

{
  "alg":"ES256",
  "typ":"JWT",
  "kid":"123"
}
{
    "sub": "1234567890",
    "iss": "nefilim",
    "name": "John Doe",
    "admin": true,
    "iat": 1516239022
}

Signing a JWT

Following on from above:

jwt.sign(ecPrivateKey)

returns an Either<JWTVerificationError, SignedJWT<JWSES256Algorithm>>. The rendered field in the SignedJWT contains the encoded string representation, in this case:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoibmVmaWxpbSIsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjJ9.glaZCoqhNE7TiPLZl2hDK18yZGJUyVW0cE8pTM-zggyVfROiMPQJlImVcPSxTd50A8NRDOhoZwrqX04K4QS1bQ

Decoding a JWT

JWT.decode("eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoibmVmaWxpbSIsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjJ9.glaZCoqhNE7TiPLZl2hDK18yZGJUyVW0cE8pTM-zggyVfROiMPQJlImVcPSxTd50A8NRDOhoZwrqX04K4QS1bQ")

The resulting DecodedJWT contains a JWT<JWSES256Algorithm> and the individual (3) parts of the JWT. Public claims can be accessed via the predefined accessors, eg:

JWT.decode("...").tap { 
    println("the issuer is: ${it.issuer()}")
    println("the subject is: ${it.subject()}")
}

private claims be accessed with claimValue/claimValueAsBoolean/claimValueAsLong etc.

Validating a JWT

Custom claim validators can be created by defining ClaimsValidator:

typealias ClaimsValidatorResult = ValidatedNel<out JWTVerificationError, JWTClaims>
typealias ClaimsValidator = (JWTClaims) -> ClaimsValidatorResult

eg. a claim validator for issuer could look like this:

fun issuer(issuer: String): ClaimsValidator = requiredOptionClaim( // an absent claim would be considered an error
    "issuer", // a label for the claim (used in error reporting) 
    { issuer() }, // a function that returns the claim from the JWTClaims/JWT 
    { it == issuer }, // the predicate to evaluate the claim value 
    JWTValidationError.InvalidIssuer // the error to return 
)

and for a private claim:

fun issuer(issuer: String): ClaimsValidator = requiredOptionClaim( // an absent claim would be considered an error
    "admin", // a label for the claim (used in error reporting) 
    { claimValueAsBoolean("admin") }, // a function that returns the claim from the JWTClaims/JWT 
    { it == true }, // the predicate to evaluate the claim value 
)

in this case the ValidationNel would contain JWTValidationError.RequiredClaimIsMissing("admin") if the claim was absent in the JWT or JWTValidationError.RequiredClaimIsInvalid("admin") in case it predicate failed (the value was false).

ClaimValidators can be composed using fun validateClaims(...), eg:

fun standardValidation(claims: JWTClaims): ValidatedNel<out JWTVerificationError, JWTClaims> =
    validateClaims(notBefore, expired, issuer("thecompany"), subject("1234567890"), audience("http://thecompany.com"))
(claims)

Predefined claim validators are bundled for these public claims:

  • issuer
  • subject
  • audience
  • expired
  • notbefore

Verifying a Signature

verifySignature<JWSRSAAlgorithm>("eyJhbGci...", publicKey)

Not the type needs to be specified explicitly and will limit the publicKey parameter to the allowable types. Eg, in this case it must be an RSAPublicKey.

Claim Validation and Verifying together

Combining claim validation and signature verification into one step can be done using the corresponding fun verify(...) (once again, the type parameter is required):

val standardValidation: ClaimsValidator = { claims ->
    validateClaims(
        notBefore, 
        expired, 
        issuer("thecompany"), 
        subject("1234567890"), 
        audience("http://thecompany.com")
    )(claims)
}

verify<JWSES256Algorithm>("eyJhbGci...", publicKey, standardValidation)

The resulting typealias ClaimsValidatorResult = ValidatedNel<out JWTVerificationError, JWTClaims> will either contain all the validation problems or the valid JWT.

GitHub

https://github.com/nefilim/kjwt
Comments
  • 1. get JsonArray from claims

    I have a claimSet in the JWT that is seen as a JsonArray (even though in the end its just a list of Strings).

    However, there is no claimValue function for that since they all check for jsonPrimitive?

    This leads to the exception:

    java.lang.IllegalArgumentException: Element class kotlinx.serialization.json.JsonArray is not a JsonPrimitive

    Is there any way to get that? In the end it is just a StringList but even claimValueAsList throws that Exception.

    Reviewed by TobiasReich at 2022-01-17 12:05
  • 2. Cannot access class 'arrow.core.Either'.

    In my Android project, the gradle configuration I use looks like that:

    implementation 'io.github.nefilim.kjwt:kjwt-core:0.4.0'

    but somehow the decode function doesn't seem to work for me. I always get the message:

    Cannot access class 'arrow.core.Either'. Check your module classpath for missing or conflicting dependencies

    No matter whether I use decode or decodeT and it's not like I have any other jwt library added or such. Am I missing something? Is this probably not compatible with Android?

    Reviewed by TobiasReich at 2022-01-17 11:08
  • 3. Update dependency gradle to v7.3.2

    This PR contains the following updates:

    | Package | Update | Change | |---|---|---| | gradle (source) | patch | 7.3.1 -> 7.3.2 |


    Release Notes

    gradle/gradle

    v7.3.2

    This is a patch release for Gradle 7.3.

    It fixes the following issues:

    • #​19300 Mitigations for log4j vulnerability in Gradle builds
    • #​19257 Incremental java compilation fails when renaming classname with $ character

    We recommend users upgrade to 7.3.2 instead of 7.3.

    Given the context of the Log4Shell vulnerability, make sure you take a look at our blog post on this topic.

    Upgrade Instructions

    Switch your build to use Gradle 7.3.2 by updating your wrapper:

    ./gradlew wrapper --gradle-version=7.3.2
    

    See the Gradle 7.x upgrade guide to learn about deprecations, breaking changes and other considerations when upgrading to Gradle 7.3.2.

    Reporting Problems

    If you find a problem with this release, please file a bug on GitHub Issues adhering to our issue guidelines. If you're not sure you're encountering a bug, please use the forum.


    Configuration

    📅 Schedule: At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, click this checkbox.

    This PR has been generated by Renovate Bot.

    Reviewed by nefilim at 2021-12-17 16:57
  • 4. Configure Renovate

    Welcome to Renovate! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.

    🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.


    Detected Package Files

    • .github/workflows/pushpr.yaml (github-actions)
    • gradle.properties (gradle)
    • settings.gradle.kts (gradle)
    • build.gradle.kts (gradle)
    • buildSrc/build.gradle.kts (gradle)
    • core/build.gradle.kts (gradle)
    • google-kms-grpc/build.gradle.kts (gradle)
    • jwks/build.gradle.kts (gradle)
    • gradle/wrapper/gradle-wrapper.properties (gradle-wrapper)

    What to Expect

    With your current configuration, Renovate will create 1 Pull Request:

    Update dependency gradle to v7.3.2
    • Schedule: ["at any time"]
    • Branch name: renovate/gradle-7.x
    • Merge into: main
    • Upgrade gradle to 7.3.2

    ⚠ Dependency Lookup Warnings ⚠

    Please correct - or verify that you can safely ignore - these lookup failures before you merge this PR.

    • Failed to look up dependency RELEASE:FINAL

    Files affected: build.gradle.kts


    ❓ Got questions? Check out Renovate's Docs, particularly the Getting Started section. If you need any further assistance then you can also request help here.


    This PR has been generated by Renovate Bot.

    Reviewed by nefilim at 2021-12-17 16:53
  • 5. Android support only API 26+

    And maybe a heads up for everyone who wants to use this for an Android Project.

    Since the Base64 class is used, it allows the usage of this library only from Android API 26+.

    https://developer.android.com/reference/java/util/Base64

    Versions below will get trouble with the call fun jwtDecodeString(data: String): String = String(Base64.getUrlDecoder().decode(data)) stating a NoClassDefFoundError: Failed resolution of: Ljava/util/Base64;

    Maybe it would be good to mention this in case someone is interested in this.

    Reviewed by TobiasReich at 2022-01-19 11:29
  • 6. Getting all Claims

    Sorry for bothering but is there a way to get all claims from a JWT?

    There is sadly only an option for getting single fields from it but is there access to the claimSet directly? It is set to private but it could come handy to get the whole map.

    Reviewed by TobiasReich at 2022-01-19 10:48
Related tags
Exercises for Functional Programming learning in Kotlin with Arrow

Exercises for Functional Programming in Kotlin with Arrow-kt Exercises and practice code for Functional Programming learning in Kotlin with Arrow Requ

Nov 11, 2021
🚆 Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either.
🚆 Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either.

Retrofit Adapters ?? Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either. Sandwich If you're intere

Aug 12, 2022
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

Aug 13, 2022
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

Apr 27, 2021
Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, servers & documentation.

Arrow Endpoint Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, server

Aug 3, 2022
Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, servers & documentation.

Arrow Endpoint Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, server

Oct 11, 2021
Functional Constructs for Databinding + Kotlin + RxJava

ObservableFlow Pt 1/3 Pt 2/3: Stepper Indicator Pt 3/3: SugarPreferences Functional Kotlin constructs like map(), filter() and 12 more functions built

Aug 19, 2019
Kotlin and Java API for generating .swift source files.

SwiftPoet SwiftPoet is a Kotlin and Java API for generating .swift source files. Source file generation can be useful when doing things such as annota

Aug 5, 2022
Kotlin tooling for generating kotlinx.serialization serializers for serializing a class as a bitmask

kotlinx-serialization-bitmask Kotlin tooling for generating kotlinx.serialization serializers for serializing a class as a bitmask. Example @Serializa

May 29, 2022
Ivy FRP is a Functional Reactive Programming framework for declarative-style programming for Android

FRP (Functional Reactive Programming) framework for declarative-style programming for Andorid. :rocket: (compatible with Jetpack Compose)

Jul 8, 2022
An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit
An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit

Random-Quote-Generator An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit MAD Score Te

Jan 8, 2022
An experimental UI toolkit for generating PowerPoint presentation files using Compose
An experimental UI toolkit for generating PowerPoint presentation files using Compose

ComposePPT An experimental UI toolkit for generating PowerPoint presentation files(.pptx) using Compose. Inspired by Glance and Mosaic. Why? This proj

Aug 10, 2022
Kotlin-client-dsl - A kotlin-based dsl project for a (Client) -> (Plugin) styled program

kotlin-client-dsl a kotlin-based dsl project for a (Client) -> (Plugin) styled p

Feb 3, 2022
Android app with minimal UI to run snowflake pluggable transports proxy, based on library IPtProxy

Simple Kotlin app for testing IPtProxy's snowflake proxy on Android Essentially a button for starting and stopping a Snowflake Proxy with the default

Jun 26, 2022
Download tool based on kotlin and coroutine.
Download tool based on kotlin and coroutine.

DownloadX A multi-threaded download tool written with Coroutine and Kotlin Read this in other languages: 中文, English, Changelog Prepare Add jitpack re

Aug 11, 2022
🎬 A demo project for The Movie DB based on Kotlin MVVM architecture and material design & animations.
🎬 A demo project for The Movie DB based on Kotlin MVVM architecture and material design & animations.

TheMovies A simple project for The Movie DB based on Kotlin MVVM clean architecture and material design & animations. How to build on your environment

Aug 5, 2022
GraphQL based Jetpack Compose and SwiftUI Kotlin Multiplatform sample
GraphQL based Jetpack Compose and SwiftUI Kotlin Multiplatform sample

GraphQL based Jetpack Compose and SwiftUI Kotlin Multiplatform sample

Aug 13, 2022
Beautifully designed Pokémon Database app for Android based on PokéAPI and powered by Kotlin.
Beautifully designed Pokémon Database app for Android based on PokéAPI and powered by Kotlin.

PokéFacts PokéFacts is an open-source Pokémon Database app for Android based on PokéAPI and powered by Kotlin. The app allows users to view Pokémons,

May 25, 2022
A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Aug 8, 2022