Ktor 3.6.0 turns the auth provider into a value the route can type-check
The typed authentication module and Netty's HTTP/3 support both arrive experimental in Ktor 3.6.0, while the changes that touch code you already wrote are two deprecations and one client Accept-header setting.
The Engineer · Build desk
What happened
- JetBrains released Ktor 3.6.0 with typed authentication, specialized OpenID Connect support and HTTP/3 for the Netty engine, all of it described in the announcement as experimental.
- The typed authentication module also covers role-based access checks and anonymous users, alongside the type safety it adds for complex authentication setups.
- Netty's HTTP/3 runs over QUIC, and its opt-in block tunes flow-control limits, UDP socket configuration and an idle timeout that JetBrains sets to 30 seconds in the sample.
- The same engine can now serve cleartext h2c on one connector and HTTP/2 over TLS on another, switched on with enableH2c = true and enableHttp2 = true.
Why it matters
- Who has to decide decision The experimental label sets the scope of a sensible test: one service on a branch, with no promise the API arrives unchanged in the next minor release.
- What it costs cost HTTP/3 costs more than a dependency bump. Because QUIC travels over UDP, whoever runs the load balancers and firewalls has to open the same port for UDP before the listener is reachable.
- What it limits constraint A team that adopts none of the experimental features still has edits to schedule, because two functions their code may call today are now deprecated.
- What's now possible capability A service that currently hand-rolls token refresh and login redirects can test a library path for it, with sessions and a browser login interface included.
Next story · Build · Oct 7
JetBrains now recommends Kotlin Toolchain 0.13 for most new multiplatform projectsOne report1 publisher
`authenticateWith(jwtAuth)` takes the provider, not its name. In the sample JetBrains published, `jwt<User>("my-jwt")` is assigned to a val, and the route block references that val, so the principal type travels with it: the sample reads `val user = call.principal`, with no type argument and no cast [12]. JetBrains describes the older arrangement as implicit typing bridging configuration to routes, and the new module as types that guarantee full type safety when working with complex authentication [2].
The elegant part of that syntax depends on Kotlin context parameters, according to the release post [4]. So the Kotlin version in your build constrains this upgrade as much as the Ktor version does.
The OpenID Connect plugin follows the same shape. `install(Oidc)` returns a value, `identityProvider("auth0")` declares an issuer and a `bearer` block with an audience set, and the route authenticates against `auth0.jwtBearer` before reading `call.principal.claims.subject` [5]. Providers are typed, and JetBrains says the plugin supports all OpenID Connect features in a typed way [5].
`enableHttp3 { }` does not stand alone. The documented order is an SSL connector first, then the opt-in block [7]; in the sample that connector listens on port 8443 [11]. QUIC is carried over UDP, which is why the block exposes UDP socket configuration at all [8], and it means 8443 has to be open for UDP on every hop in front of the service, not just TCP. JetBrains did not publish latency or throughput figures for the HTTP/3 path, so there is no vendor number here to test against your own traffic. Feedback is explicitly requested [9], which is the polite way of saying the API can still move.
The two deprecations are ordinary maintenance. `ApplicationCall.receive()` now accepts nullable types, and `receiveNullable()` is deprecated in consequence [15]. `respondHtmlPartial` replaces the deprecated `respondHtmlFragment` and uses `TagConsumer<Appendable>`, so it can emit partial HTML with every element `FlowContent` supports [16]. Parameter conversion also covers Kotlin's `Uuid`, `Byte` and the unsigned numeric types, which makes `val id: Uuid by call.parameters` work in a route [14].
On the client side, ContentNegotiation used to merge its registered content types into every `Accept` header [17]. With `ContentTypeMergeStrategy.SkipIfPresent`, an explicit `Accept` header wins, and requests without one still get the registered types added [17]. Anyone carrying an interceptor to undo that merge for one strict API can drop it. The cheap way to test the rest is a throwaway project from the generator at start.ktor.io with one protected route [10].
What to watch
- Whether the typed authentication module and Oidc plugin keep their current API shape in 3.7 or churn before the experimental label comes off.
- Whether JetBrains publishes measurements, or a production-support statement, for the Netty QUIC path.
- Whether receiveNullable() and respondHtmlFragment get removal versions in a following release.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence62
- Adoption15
- Hype gap+34
- Incentives70
- Confidence60
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
JetBrains announced Ktor 3.6.0 as available, describing the release as full of new experimental features including typed authentication capabilities with specialized support for OpenID Connect and HTTP/3 support for the Netty engine, plus quality-of-life improvements for routing and request handling and more convenient defaults for Kotlin Multiplatform clients.
- [2]
According to the release post, Ktor's authentication had until now relied on implicit typing to bridge configuration to the routes; the new module provides types to guarantee full type safety when working with complex authentication.
- [3]
The typed authentication module also supports role-based access and anonymous users.
- [4]
JetBrains wrote that by leveraging context parameters, the team was able to ensure even more elegant syntax in the typed authentication module.
- [5]
The new OpenID Connect (Oidc) plugin allows creation of typed authentication providers that support all OpenID Connect features in a typed way; the sample uses val oidc = install(Oidc), oidc.identityProvider("auth0") { issuer = "https://my-tenant.auth0.com"; bearer { audience = setOf("https://api.example.com") } }, then authenticateWith(auth0.jwtBearer) and call.principal.claims.subject.
- [6]
The Oidc plugin includes support for sessions and a browser login interface with auto-refreshing tokens.
- [7]
The Netty server engine now has experimental HTTP/3 support over QUIC; to enable it you configure an SSL connector, then opt in with enableHttp3 { }.
- [8]
The enableHttp3 block sets QUIC-specific settings such as flow-control limits and UDP socket configuration; the sample sets quicMaxIdleTimeout = 30.seconds.
- [9]
JetBrains states the HTTP/3 support is still experimental and asks for feedback from anyone who tries it.
- [10]
JetBrains points readers to the interactive project generator at start.ktor.io to start a project on Ktor 3.6.0.
- [11]
In the HTTP/3 sample, the sslConnector is configured with a keyStore, keyAlias "server", a keyStorePassword lambda returning "changeit", and port = 8443.
- [12]
The published sample assigns a provider to a value, val jwtAuth = jwt<User>("my-jwt") { verifier(jwkProvider, issuer) validate { ... } }, and the route block uses authenticateWith(jwtAuth) { get("/profile") { val user = call.principal ... } }.
- [13]
A Netty server can now serve h2c on one connector and HTTP/2 over TLS on another, enabled with enableH2c = true and enableHttp2 = true.
- [14]
Request-parameter conversion now supports Kotlin's Uuid, Byte and unsigned numeric types, as in the sample's val id: Uuid by call.parameters.
- [15]
ApplicationCall.receive() now also accepts nullable types, making the route contract explicit and deprecating receiveNullable().
- [16]
The release adds respondHtmlPartial, which replaces the deprecated respondHtmlFragment and uses TagConsumer<Appendable>, so it can respond with unrestricted partial HTML including all elements supported by FlowContent.
- [17]
The client ContentNegotiation plugin used to merge its registered content types into every Accept header; with ContentTypeMergeStrategy.SkipIfPresent an explicit Accept header wins, and when a request has no Accept header the plugin continues to add the registered content types.
- [18]
The Ktor 3.6.0 announcement names two deprecated functions that existing code may call: receiveNullable() and respondHtmlFragment.
Sources
2 independent publishers whose own reporting we read for this story.
- blog.jetbrains.comKtor 3.6.0 Is Now Available!
1 article · September 18, 2026
- dev.toJetBrains Releases Ktor 3.6.0 with HTTP/3 & OpenID Connect Support
1 article · September 18, 2026
Topics and entities
Topics
- Web framework releases
- HTTP/3 and QUIC adoption
- API deprecations
- Type-safe authentication