Ktor
Building Asynchronous Servers and Clients in Kotlin
Ktor 3.6.0 Is Now Available!
Ktor 3.6.0 is here! This release is full of new experimental features, including typed authentication capabilities with specialized support for OpenID Connect and HTTP/3 support for the Netty engine. There are also a few quality-of-life improvements for routing and request handling, more convenient defaults for Kotlin Multiplatform clients, and more. Check out What’s new in Ktor 3.6.0 on our website for the full list of changes, or review the release notes.
🚀 Get started with Ktor 3.6.0
Ready to explore Ktor 3.6.0? Start your next project with the interactive project generator at start.ktor.io. Your feedback and contributions are always welcome!
Typed authentication
Until now, Ktor’s authentication has relied on implicit typing to bridge configuration to the routes. In this module, you get new types to guarantee full type safety when working with complex authentication. It also supports role-based access and anonymous users. By leveraging context parameters, we were able to ensure even more elegant syntax. Read the type-safe authentication documentation for setup, role checks, and failure handling.
val jwtAuth = jwt<User>("my-jwt") {
verifier(jwkProvider, issuer)
validate { credential ->
val payload = credential.payload
User(
id = payload.subject,
email = payload.getClaim("email").asString()
)
}
}
routing {
authenticateWith(jwtAuth) {
get("/profile") {
val user = call.principal
call.respondText(user)
}
}
}
OpenID Connect
The new OpenID Connect (Oidc) plugin aims to reduce complexity when securing your service through OpenID Connect Providers. The Oidc plugin allows you to create typed authentication providers that support all OpenID Connect features in a typed way. There is also support for sessions, a browser login interface with auto-refreshing tokens, and more. For the full documentation, check out the Ktor website – here.
suspend fun Application.module() {
val oidc = install(Oidc)
val auth0 = oidc.identityProvider("auth0") {
issuer = "https://my-tenant.auth0.com"
bearer {
audience = setOf("https://api.example.com")
}
}
routing {
authenticateWith(auth0.jwtBearer) {
get("/orders") {
val subject = call.principal.claims.subject
call.respondText("Hello $subject")
}
}
}
}
More Netty features
The Netty server engine now has experimental HTTP/3 support over QUIC. To enable it, configure an SSL connector, then opt in with enableHttp3 { }:
embeddedServer(Netty, environment, {
sslConnector(
keyStore = keyStore,
keyAlias = "server",
keyStorePassword = { "changeit".toCharArray() },
privateKeyPassword = { "changeit".toCharArray() }
) { port = 8443 }
enableHttp3 { quicMaxIdleTimeout = 30.seconds }
}) { /* application */ }.start(wait = true)
The enableHttp3 {} block also lets you tune QUIC-specific settings, such as flow-control limits and UDP socket configuration. It is still experimental, so we would love your feedback if you decide to try it.
A Netty server can now also serve h2c on one connector and HTTP/2 over TLS on another. Enable both with enableH2c = true and enableHttp2 = true.
embeddedServer(Netty, configure = {
connector { port = 8080 }
sslConnector(...) { port = 8443 }
enableHttp2 = true
enableH2c = true
}) { /* application */ }.start(wait = true)
More efficient route handlers
Request-parameter conversion now supports Kotlin’s Uuid, Byte, and unsigned numeric types. ApplicationCall.receive() now also accepts nullable types, making the route contract explicit and deprecating receiveNullable().
put("/users/{id}") {
val id: Uuid by call.parameters
val preferences = call.receive<NotificationPreferences?>()
if (preferences == null) {
preferenceService.clear(id)
} else {
preferenceService.update(id, preferences)
}
call.respond(HttpStatusCode.NoContent)
}
We have also added respondHtmlPartial, which replaces the deprecated respondHtmlFragment. The new function uses TagConsumer<Appendable>, so it can respond with unrestricted partial HTML – with all elements supported by FlowContent.
get("/status") {
call.respondHtmlPartial(HttpStatusCode.OK) {
td { +"Ready" }
}
}
More control over ContentNegotiation
The client ContentNegotiation plugin used to merge its registered content types into every Accept header. That is usually helpful, but not when an API expects the header you set on a request to remain exactly as it is.
With ContentTypeMergeStrategy.SkipIfPresent, an explicit Accept header wins. When a request has no Accept header, the plugin continues to add the registered content types as usual:
install(ContentNegotiation) {
register(ContentType.Application.Json, noOpJsonConverter)
acceptHeaderMergeStrategy = ContentTypeMergeStrategy.SkipIfPresent
}
Simple Kotlin Multiplatform clients
Ktor 3.6.0 introduces ktor-client-engine-defaults: a curated set of client engines for Kotlin Multiplatform projects. Add it to commonMain, and create an HttpClient() without choosing an engine in shared code. Ktor selects the appropriate available engine for each target.
The HTTP cache has moved in the same direction. File-based cache storage now uses the Path of kotlinx-io, so persistent HttpCache storage is no longer limited to JVM java.io.File APIs. Together, these improvements make setting up a KMP client with a simple cache significantly simpler:
// build.gradle.kts
kotlin {
sourceSets {
commonMain {
dependencies {
api("io.ktor:ktor-client-engine-defaults:3.6.0")
}
}
}
}
// Main.kt
val client = HttpClient() {
install(HttpCache) {
publicStorage(FileStorage(Path("build/cache")))
}
}
This gives Ktor projects a more natural common-code setup while retaining the option to choose and configure a specific engine whenever a platform needs it.
For the full list of 3.6.0 changes, including WebRTC support for JVM, asynchronous DNS resolution for CIO, OpenAPI tag descriptions, duplicate-cookie parsing, and JavaScript fetch() overrides, see What’s New in Ktor 3.6.0.
🙏 Thank you!
Thank you to everyone in the community for the feedback, issue reports, and contributions that help make every Ktor release better. A special thank-you to the external contributors whose work is included in the release: kdelay, Rafa Ruiz, and solo.
Start building your next project at start.ktor.io. Your suggestions and contributions are always welcome!
👉 Get Started With Ktor | 💬 Join the community on Slack