I’ve recently been picking up Kotlin and, since I work with authentication and authorization protocols on a daily basis, I used a basic OAuth scenario as my learning activity and thought I'd share my journey.
The scenario was to issue an OAuth request, parse the results, and then access a protected resource using the resulting token. This is not using any of the browser based grant types, instead just back end communication using the token endpoint and the client credentials grant type.
I’m not a Java developer, so this use of Kotlin has also been my first experience with that entire eco system. As a result, this article will include how to set up a new project, mainly for my own benefit. Click here to skip straight to the Kotlin, and bear with me as I stumble around outside of my comfort zone.
Project Setup
I’m a .NET developer, used to having my hand held by Visual Studio and productivity increased by ReSharper.
As a result, I used IntelliJ as my IDE, where Kotlin is supported out of the box and alt
+enter
still reigns.
- Once you spin up IntelliJ, create a new gradle project with support for Kotlin (Java)
- Give your project an ArtifactId (this is typically in reverse DNS format, so something like
com.scottbrady91.kotlin.oauth
) - Check “Use auto-import” (I also had to use a local distribution of Gradle in order to support the Java 9.0.1 SDK I foolishly installed)
- Create the project
You should now have a project with at least a src/main/kotlin
folder structure, a build.gradle & settings.gradle files.
If you don’t see gradle build and then a src/main/kotlin folder be created, you may have to fix whatever is wrong with Gradle when it treid to build, and then recreate the project.
We can confirm everything is good by adding a Kotlin file to our kotlin folder, giving it a main function, and running it:
fun main(args: Array<String>) {
println("Hello Kotlin!")
}
Kotlin OAuth Request
First thing we want to do is to gather the required parameters for issuing our token request. So, let’s create a new function that accepts what we need to make a token request.
fun getClientCredential(
tokenEndpoint: String,
clientId: String,
clientSecret: String,
scopes: List<String>) {
}
Making a HTTP Request with Fuel
Now we want to make a post request to the token endpoint. I wanted to use only Kotlin libraries for this, so I chose Fuel as my HTTP networking library. We can add this library by updating build.gradle to look like the following (using the latest version numbers and not in the buildscript block):
repositories {
mavenCentral()
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "com.github.kittinunf.fuel:fuel:1.11.0"
}
Now we can call httpPost
on our tokenEndpoint (a string), and get the response as a string:
tokenEndpoint.httpPost().responseString()
OAuth expects our token request to use the application/x-www-form-urlencoded
content type, which fuel supports by simply passing httpPost
a list of pairs of string & any.
Making our post look like:
tokenEndpoint.httpPost(listOf(
"grant_type" to "client_credentials",
"client_id" to clientId,
"client_secret" to clientSecret,
"scope" to scopes.joinToString(" ")))
.responseString()
Now we need to grab the response.
I haven’t delved into async Kotlin yet, so I’m going to use the synchronous version of httpPost
.
However, the difference in code is trivial:
val (request, response, result) = tokenEndpoint.httpPost(listOf(
"grant_type" to "client_credentials",
"client_id" to clientId,
"client_secret" to clientSecret,
"scope" to scopes.joinToString(" ")))
.responseString()
when (result) {
is Result.Success -> {
// handle response
}
is Result.Failure -> {
// handle error
}
}
Async
tokenEndpoint.httpPost(listOf(
"grant_type" to "client_credentials",
"client_id" to clientId,
"client_secret" to clientSecret,
"scope" to scopes.joinToString(" ")))
.responseString { request, response, result ->
when (result) {
is Result.Success -> {
// handle response
}
is Result.Failure -> {
// handle error
}
}
}
when
& is
are Kotlin’s version of switch
& case
.
We could just as easily have used if (result is Result.Success)
Basic Authentication
Client credentials can also be sent using basic authentication.
Again, this is supported by Fuel using the authenticate
function, supplying the username and password arguments with our client id and secret.
tokenEndpoint.httpPost(listOf(
"grant_type" to "client_credentials",
"scope" to scopes.joinToString(" ")))
.authenticate(clientId, clientSecret)
.responseString()
Parsing JSON using Klaxon
Let’s create an object to represent our token response, and have our function return:
class TokenResponse(
var token: String,
var expiresIn: Int,
var tokenType: String)
Now we actually need to do something with the JSON response from our token endpoint, and we’re going to do this using the Kotlin JSON parsing library Klaxon.
This library again uses JCenter, so all we need to do is add the following to our build.gradle file:
dependencies {
// other dependencies
compile "com.beust:klaxon:0.32"
}
Now we can parse our response into a TokenResponse
:
val parser = Parser()
val json = parser.parse(StringBuilder(result.value)) as JsonObject
TokenResponse(
json["access_token"].toString(),
json["expires_in"].toString().toInt(),
json["token_type"].toString())
We can then return this TokenResponse
from our function (for error handling I’m just throwing an exception but we'll revisit this).
Calling a Protected Resource
Now that we have an access token, we can call an API that is protected by our authorization server. To do this, we can again use Fuel to make our HTTP request, and simply add an Authorization header, like so:
fun callApi(apiEndpoint: String, tokenType: String, token: String): String {
val (request, response, result) = apiEndpoint
.httpGet()
.header(Pair("Authorization", "$tokenType $token"))
.responseString()
return when (result) {
is Result.Success -> {
result.value
}
is Result.Failure -> {
"error!"
}
}
}
Now we can call our two functions in succession:
val response = getClientCredential("http://localhost:5000/connect/token", "kotlin_oauth", "client_password", listOf("api1.read", "api1.write"))
val apiResponse = callApi("http://localhost:5000/api", response.tokenType, response.token)
IdentityServer 4
For my authorization server I used IdentityServer 4, an OpenID Connect & OAuth framework that runs on ASP.NET Core.
You can find an example implementation in the accompanying GitHub repo for this article.
To run the application, simply run dotnet run
in the AuthorizationServer directory.
This authorization server also includes a demo API that requires a JWT issued by the authorization server.
Making things a little more “Kotlin” with the Either Monad
I was sure things could be made a little more “Kotlin”, so I asked my colleague (boss) Kevin Jones about how to improve my two functions and handle both result and errors at the same time. He put me on to “Either”, which then proceeded to make my head hurt. I prefer learning by looking at other existing implementations and these are the two that I found the most useful:
This led me to create the following class to map a result, where Left is success and Right is failure:
sealed class Response<out L, R> {
class Success<out L, R>(val value: L) : Response<L, R>()
class Failure<out L, R>(val error: R) : Response<L, R>()
fun <X> bind(success: (L) -> (Response<X, R>)): Response<X, R> {
return when (this) {
is Response.Success<L, R> -> success(this.value)
is Response.Failure<L, R> -> Failure(this.error)
}
}
companion object {
fun <L, R> of(response: L) = Success<L, R>(response)
fun <L, R> error(error: R) = Failure<L, R>(error)
}
}
If I’ve broken a cardinal rule with this implementation, let me know and I’ll update the article and buy you a beer if we ever meet.
We can now update our token call and API call to return Response<TokenResponse, String>
and Response<String, String>
respectively, and update our functions to return results like so:
return when (result) {
is Result.Success -> {
Response.of(/*Return Type*/)
}
is Result.Failure -> {
return Response.error(/*error message*/)
}
}
And can now call both functions with the following, calling bind to chain functionality:
val response =
getClientCredential(
"http://localhost:5000/connect/token",
"kotlin_oauth",
"client_password",
listOf("api1.read", "api1.write"))
.bind({ tokenResponse ->
callApi("http://localhost:5000/api", tokenResponse.tokenType, tokenResponse.token) })
when (response) {
is Response.Success -> {
println(response.value)
}
is Response.Failure -> {
println(response.error)
}
}
Handling the final result and errors from either function by parsing the result, as per usual.
Now, if the first function fails, the second function won’t run, and we’ll receive the error from the first call in our result. I think that’s pretty neat!
Resource Owner Password Credentials
We can add support for the ROPC grant type by simply adding username and password to our OAuth request. I’m not going to show this, as I won’t sully my blog with the ROPC grant type. Please, don’t use this grant type.
Source Code
You can find the completed source code for this exercise on GitHub. This includes a demo authorization server and protected resource running on .NET Core.
If you're interested in learning more about Kotlin, check out the Kotlin Fundamentals course on Pluralsight, authored by my colleague, Kevin Jones.
If you want to learn about the other side of the story, check out JSON Web Token Verification in Ktor using Kotlin and Java-JWT, where we protect an API using bearer tokens. Next experiment is to make some OIDC requests and validate some tokens!