Skip to content

LukasForst/ktor-plugins

Repository files navigation

Ktor Plugins

GitHub release (latest SemVer)

Collection of useful Ktor plugins. All plugins are hosted on Maven central and have same version that should be similar to the latest version of Ktor. The plugins can be added to your project as easy as:

implementation("dev.forst", "ktor-<plugin>", "<latest version>")

Ktor API Key Authentication Provider

Simple authentication provider for Ktor that verifies presence of the API key in the header. Useful if you want to use X-Api-Key or similar approaches for request authentication.

/**
 * Minimal Ktor application with API Key authentication.
 */
fun Application.minimalExample() {
    // key that will be used to authenticate requests
    val expectedApiKey = "this-is-expected-key"

    // principal for the app
    data class AppPrincipal(val key: String) : Principal
    // now we install authentication feature
    install(Authentication) {
        // and then api key provider
        apiKey {
            // set function that is used to verify request
            validate { keyFromHeader ->
                keyFromHeader
                    .takeIf { it == expectedApiKey }
                    ?.let { AppPrincipal(it) }
            }
        }
    }

    routing {
        authenticate {
            get {
                val p = call.principal<AppPrincipal>()!!
                call.respondText("Key: ${p.key}")
            }
        }
    }
}

Ktor Content Security Policy

Plugin that allows setting Content-Security-Policy headers.

/**
 * Minimal Ktor application using Content Security Policy.
 */
fun Application.minimalExample() {
    // this sets Content-Security-Policy
    install(ContentSecurityPolicy) {
        policy { call, body ->
            when (call.request.path()) {
                "/specific" -> mapOf("default-src" to "'none'")
                "/ignored" -> null
                else -> mapOf("default-src" to "'self'")
            }
        }
    }
    // basic routing
    routing {
        get("/specific") { call.respond(HttpStatusCode.OK) }
        get("/ignored") { call.respond(HttpStatusCode.OK) }
        get("/") { call.respond(HttpStatusCode.OK) }
    }
}

Ktor OpenAPI Generator & Swagger UI

Ktor OpenAPI plugin (with support for Ktor 2.x.y) that generates OpenAPI from your routes definition and allows you to host Swagger UI. Hosted on different repository: LukasForst/ktor-openapi-generator as it is a fork of popular papsign/Ktor-OpenAPI-Generator with some refactoring and support for Ktor 2.x.y.

It supports most of the stuff from Ktor including JWT and Session auth.

/**
 * Minimal example of OpenAPI plugin for Ktor.
 */
fun Application.minimalExample() {
    // install OpenAPI plugin
    install(OpenAPIGen) {
        // this automatically servers Swagger UI on /swagger-ui
        serveSwaggerUi = true
        info {
            title = "Minimal Example API"
        }
    }
    // install JSON support
    install(ContentNegotiation) {
        jackson()
    }
    // and now example routing
    apiRouting {
        route("/example/{name}") {
            // SomeParams are parameters (query or path), SomeResponse is what the backend returns and SomeRequest
            // is what was passed in the body of the request
            post<SomeParams, SomeResponse, SomeRequest> { params, someRequest ->
                respond(SomeResponse(bar = "Hello ${params.name}! From body: ${someRequest.foo}."))
            }
        }
    }
}

data class SomeParams(@PathParam("who to say hello") val name: String)
data class SomeRequest(val foo: String)
data class SomeResponse(val bar: String)

Ktor Rate Limiting

A simple library that enables Rate Limiting in Ktor. Note that Ktor now offers their own rate limiting plugin since 2.2.0, we recommend using their implementation.

/**
 * Minimal Ktor application with Rate Limiting enabled.
 */
fun Application.minimalExample() {
    // install feature
    install(RateLimiting) {
        registerLimit(
            // allow 10 requests
            limit = 10,
            // each 1 minute
            window = Duration.ofMinutes(1)
        ) {
            // use host as a key to determine who is who
            call.request.origin.host
        }
        // and exclude path which ends with "excluded"
        excludeRequestWhen {
            call.request.path().endsWith("excluded")
        }
    }
    // now add some routes
    routing {
        get {
            call.respondText("Hello ${call.request.origin.host}")
        }
        get("excluded") {
            call.respondText("Hello ${call.request.origin.host}")
        }
    }
}