JWT Token using Ktor and Kotlin
Tags: webdev, kotlin, beginners
Published: 2024-06-02 21:28:47

## The backstory
I am a big lover of Kotlin since I saw it 2018. Since then I always dreamed about having it everywhere just like we have JavaScript for everything today: on the mobile, front - end, back - end and everything else. Given that we already have Multi Platform Compose, we are already set on the front - end part and mobile part but still waiting for the server side Kotlin. Then Ktor was introduced which gave us the possibility to write server side pretty fast and neatly ( see my previous post for how blazingly fast it is) and it is the time to talk about security. In this article I managed to create a jwt token authentication ( this is done with the help of youtube video : [https://www.youtube.com/watch?v=LWVgof52BBg](url))

## Setting up the project
Using Ktor project setup you just have to add Authenticate and AuthenticateJWT packages to be able to use them. Next, inside the application.conf file ( yaml does not work for some reason) add this block:

```kotlin
jwt{
  domain= "https://jwt-provider-domain/"
  audience= "jwt-audience"
  realm= "ktor sample app"
  secret= "myLittleSecret"
  issuer= "kotr sample app"
}
```
## Steps
First step, let us load our configuration variables from the config file in the Application.kt

```kotlin
 val config = HoconApplicationConfig(ConfigFactory.load())
    val secret = config.property("jwt.secret").getString()
    val issuer = config.property("jwt.issuer").getString()
    val audience = config.property("jwt.audience").getString()
    val myRealm = config.property("jwt.realm").getString()
    configureSerialization()
    configureDatabases()
    configureHTTP()
    configureSecurity(secret = secret, issuer = issuer, audience = audience, myRealm = myRealm)
    configureRouting(secret = secret, issuer = issuer, audience = audience)
```
As you see it, we are using these variables to configure the security and the routing as well. Secondly, we are going to configure security of our application.

```kotlin
fun Application.configureSecurity(secret: String, issuer: String, audience: String, myRealm: String) {
    install(Authentication) {
        jwt {
            realm = myRealm
            verifier(JWT.require(Algorithm.HMAC512(secret)).withAudience(audience).withIssuer(issuer).build())
            validate { jwtCredential: JWTCredential ->
                kotlin.run {
                    val userName = jwtCredential.payload.getClaim("userName").asString()
                    if (userName.isNotEmpty()) {
                        JWTPrincipal(jwtCredential.payload)
                    } else {
                        null
                    }
                }
            }
            challenge { _, _ ->
                call.respond(
                    HttpStatusCode.Unauthorized,
                    GenericResponse(isSuccess = true, data = "Token is not valid or has expired")
                )
            }
        }
    }
}
```
Let us explain what is going on here. We are "installing" the aunthentication with the jwt with our user's username as the claim payload.We are validating everything using JWTPrincipal class ( everything explained is under `validate` function. In the challenge function we are sending the response when the token is not valid.

Third and finally, we are now going to use this token. Let us go to our routes file and create two simple routes, one to get the token and the other to get the user's data using that token.

```kotlin
fun Application.JwtRoutes(secret: String, issuer: String, audience: String){
    routing {
        get("/test"){
            call.respond(HttpStatusCode.OK,GenericResponse(true,"Hello World"))
        }
        post("/token"){
            val user:UserEntity = call.receive()

            val generatedToken = JWT.create()
                .withAudience(audience)
                .withIssuer(issuer)
                .withClaim("userName", user.userName)
                .withClaim("email", user.email)
                .sign(Algorithm.HMAC512(secret))

            val token = generatedToken
            call.respond(HttpStatusCode.OK,GenericResponse(true,token))
        }

        authenticate {
            get("/token"){
                val principal = call.principal<JWTPrincipal>()
                val username = principal!!.payload.getClaim("userName").asString()
                val email = principal.payload.getClaim("email").asString()

                val user = UserEntity(1,username,"User",email,"")
                call.respond(HttpStatusCode.OK,GenericResponse(true, data =user))
            }
        }
    }
}
```
After running our server, we are first getting the token as shown below 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzuununs8zno7nydrjh4.png)

After passing it as Bearer Token into Postman and trying to get our data we get it successfully
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jeibc136ewzkhpksr8y.png)


## Conclusion
Getting started with Ktor and Kotlin is not easy as there is not much community out there that is using this technology professionally and building client software. But it does have a great potential to become a peer competitor with the technologies we love and use daily.