Securing a Kotlin API with JWTs

On this page


This tutorial explains how to integrate the jose4j security library and use it to validate JWTs in a plain Kotlin API. The minimal Spark Java REST API stack is used.


Curity Identity Server is used in this example, but other OAuth servers can also be used.

Create a Kotlin API

Create your own Kotlin API according to a Spark Tutorial of your choice. The example API has a simple entry point function which ensures that a valid access token has been provided before any API routes are allowed to run:

fun main() {
val configuration = Configuration()
val filter = OAuthFilter(configuration)
before("*") { request, response ->
filter.doFilter(request.raw(), response.raw(), null)
val routes = ApiRoutes()
get("/api/data", routes::getData)


The code example uses the Java configuration system and the following properties. The jose4j library will download token signing public keys from the Authorization Server's JWKS endpoint, then use them to verify the signature of received JWTs. The library will also reject any access tokens whose issuer and audience claims do not match the API's configured values:


Security Library Integration

The jose4j library performs the detailed security work. When the OAuth filter is created it constructs global objects to manage downloading JWKS keys:

private val _httpsJkws = HttpsJwks(_configuration.getJwksEndpoint())
private val _httpsJwksKeyResolver = HttpsJwksVerificationKeyResolver(_httpsJkws)

Note that these classes also manage caching of JWKS keys in a thread safe manner. Any subsequent requests to the API with a JWT access token having the same kid value, do not trigger additional HTTPS calls to the JWKS endpoint, so that API performance is good.

The token validation behavior is configured as follows, where allowed algorithms are restricted to those expected from clients, as recommended in JWT Security Best Practices.

val jwtConsumer = JwtConsumerBuilder()

Finally, the OAuth filter calls the jose4j processToClaims method, and for valid tokens this results in a claims object being returned. This is then attached to the request object:

val jwtClaims = jwtConsumer.processToClaims(jwt)
request.setAttribute("principal", jwtClaims)

Using Claims and Scopes

Once the token is validated, the API can access its scopes and claims via the Java request object. A real world API would then use the scopes and claims from the JWT to authorize access to business data, according to the below articles:

The example API simply echoes scopes and claims back to the client, and in this example the JWT contains a custom claim called role:

class ApiRoutes {
fun getData(request: Request, response: Response): String {
val claims = request.attribute<JwtClaims>("principal")
val role = claims.getClaimValueAsString("role")
val scope = claims.getClaimValueAsString("scope")
val data = DataResponse("API Request has role: $role and scope: $scope")
return Gson().toJson(data)

Run the API

To build and run the API, first ensure that maven and a JDK of version 17 or higher are installed. Then run the following commands, which will run the API on port 3000:

mvn package
java -jar target/secureapi-1.0-SNAPSHOT-jar-with-dependencies.jar

Test the API

Clients will call the API using HTTP requests, with an access token in the Authorization header. To do so, configure a client as described in the getting started guides, then use OAuth tools to get an access token. You can then send the token to the API using a cURL request:

curl -i http://localhost:3000/api/data -H "Authorization: Bearer eyJraWQiOiIyMTg5NTc5MTYiLC ..."

In the event of a problem validating the JWT, an error reason is returned to the client. The example returns a generic error in the WWW-Authenticate response header:

HTTP/1.1 401 Unauthorized
Date: Mon, 12 Apr 2021 16:01:07 GMT
WWW-Authenticate: Bearer, error=invalid_token, error_description=Access token is missing, invalid or expired

Logging and Error Handling

The example API also shows how to capture jose4j internal log messages and how to work with its error objects. In real world APIs this would enable any technical failures to be diagnosed, such as connecting to the authorization server.

catch (e: InvalidJwtException) {
_logger.info("JWT validation failed")
for (item in e.errorDetails) {
_logger.debug("${item.errorCode} : ${item.errorMessage}")


Integrating the jose4j library is an easy way to implement OAuth security in Java or Kotlin APIs. Once done you have full access to the access token's scopes and claims, so that your API can apply business rules, to secure access to its data.

Join our Newsletter

Get the latest on identity management, API Security and authentication straight to your inbox.

Start Free Trial

Try the Curity Identity Server for Free. Get up and running in 10 minutes.

Start Free Trial