Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to use ktor 2.0.1 #250

Merged
merged 3 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ val kotestVersion = "5.3.0"
val bouncyCastleVersion = "1.70"
val springBootVersion = "2.6.7"
val reactorTestVersion = "3.4.17"
val ktorVersion = "1.6.8"
val ktorVersion = "2.0.1"

val mavenRepoBaseUrl = "https://oss.sonatype.org"
val mainClassKt = "no.nav.security.mock.oauth2.StandaloneMockOAuth2ServerKt"
Expand Down Expand Up @@ -80,11 +80,13 @@ dependencies {
testImplementation("io.projectreactor:reactor-test:$reactorTestVersion")
testImplementation("io.ktor:ktor-server-netty:$ktorVersion")
testImplementation("io.ktor:ktor-server-sessions:$ktorVersion")
testImplementation("io.ktor:ktor-locations:$ktorVersion")
testImplementation("io.ktor:ktor-auth:$ktorVersion")
testImplementation("io.ktor:ktor-auth-jwt:$ktorVersion")
testImplementation("io.ktor:ktor-server-locations:$ktorVersion")
testImplementation("io.ktor:ktor-server-auth:$ktorVersion")
testImplementation("io.ktor:ktor-server-auth-jwt:$ktorVersion")
testImplementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
testImplementation("io.ktor:ktor-client-core:$ktorVersion")
testImplementation("io.ktor:ktor-client-jackson:$ktorVersion")
testImplementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
testImplementation("io.ktor:ktor-serialization-jackson:$ktorVersion")
testImplementation("io.ktor:ktor-client-cio:$ktorVersion")
testImplementation("io.ktor:ktor-server-test-host:$ktorVersion")
}
Expand Down
12 changes: 5 additions & 7 deletions src/test/kotlin/examples/kotlin/ktor/client/OAuth2Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.features.json.JacksonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.header
import io.ktor.http.Headers
import io.ktor.http.Parameters
import io.ktor.http.headersOf
import io.ktor.util.InternalAPI
import io.ktor.serialization.jackson.jackson
import java.nio.charset.StandardCharsets
import java.security.KeyPair
import java.security.interfaces.RSAPrivateKey
Expand All @@ -26,17 +25,16 @@ import java.util.Date
import java.util.UUID

val httpClient = HttpClient(CIO) {
install(JsonFeature) {
serializer = JacksonSerializer {
install(ContentNegotiation) {
jackson {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
}
}

@OptIn(InternalAPI::class)
suspend fun HttpClient.tokenRequest(url: String, auth: Auth, params: Map<String, String>) =
submitForm<TokenResponse>(
submitForm(
url = url,
formParameters = Parameters.build {
auth.parameters.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.auth0.jwt.JWT
import io.kotest.assertions.asClue
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import io.ktor.client.call.body
import kotlinx.coroutines.runBlocking
import no.nav.security.mock.oauth2.MockOAuth2Server
import no.nav.security.mock.oauth2.token.DefaultOAuth2TokenCallback
Expand Down Expand Up @@ -33,8 +34,8 @@ internal class OAuth2ClientTest {
)

tokenResponse.asClue {
it.accessToken.asDecodedJWT().subject shouldBe "client1"
it.accessToken.asDecodedJWT().audience.shouldContainExactly("targetScope")
it.body<TokenResponse>().accessToken.asDecodedJWT().subject shouldBe "client1"
it.body<TokenResponse>().accessToken.asDecodedJWT().audience.shouldContainExactly("targetScope")
}
}
}
Expand All @@ -56,8 +57,8 @@ internal class OAuth2ClientTest {
)

tokenResponse.asClue {
it.accessToken.asDecodedJWT().subject shouldBe "enduser"
it.accessToken.asDecodedJWT().audience.shouldContainExactly("targetScope")
it.body<TokenResponse>().accessToken.asDecodedJWT().subject shouldBe "enduser"
it.body<TokenResponse>().accessToken.asDecodedJWT().audience.shouldContainExactly("targetScope")
}
}
}
Expand Down
48 changes: 25 additions & 23 deletions src/test/kotlin/examples/kotlin/ktor/login/OAuth2LoginApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,35 @@ package examples.kotlin.ktor.login
import com.auth0.jwt.JWT
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.DeserializationFeature
import io.ktor.application.Application
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.auth.Authentication
import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.auth.authenticate
import io.ktor.auth.authentication
import io.ktor.auth.oauth
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.features.json.JacksonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.http.ContentType
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.locations.Location
import io.ktor.locations.Locations
import io.ktor.locations.location
import io.ktor.locations.locations
import io.ktor.locations.url
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.param
import io.ktor.routing.routing
import io.ktor.serialization.jackson.jackson
import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.auth.Authentication
import io.ktor.server.auth.OAuthAccessTokenResponse
import io.ktor.server.auth.OAuthServerSettings
import io.ktor.server.auth.authenticate
import io.ktor.server.auth.authentication
import io.ktor.server.auth.oauth
import io.ktor.server.engine.embeddedServer
import io.ktor.server.locations.KtorExperimentalLocationsAPI
import io.ktor.server.locations.Location
import io.ktor.server.locations.Locations
import io.ktor.server.locations.location
import io.ktor.server.locations.locations
import io.ktor.server.locations.url
import io.ktor.server.netty.Netty
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.param
import io.ktor.server.routing.routing

fun main() {
embeddedServer(Netty, port = 8080) {
Expand All @@ -53,6 +54,7 @@ fun main() {
}.start(true)
}

@OptIn(KtorExperimentalLocationsAPI::class)
fun Application.module(authConfig: AuthConfig) {

val idProviders = authConfig.providers.map { it.settings }.associateBy { it.name }
Expand Down Expand Up @@ -121,8 +123,8 @@ private fun ApplicationCall.subject(): String? {
}

internal val httpClient = HttpClient(CIO) {
install(JsonFeature) {
serializer = JacksonSerializer {
install(ContentNegotiation) {
jackson {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package examples.kotlin.ktor.login

import io.kotest.assertions.asClue
import io.kotest.matchers.shouldBe
import io.ktor.application.Application
import io.ktor.client.request.get
import io.ktor.client.request.prepareGet
import io.ktor.server.application.Application
import io.ktor.server.engine.ApplicationEngine
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
Expand Down Expand Up @@ -46,7 +46,7 @@ internal class OAuth2LoginAppTest {
}
}

private inline fun <reified R> get(url: String): R = runBlocking { httpClient.get(url) }
private inline fun <reified R> get(url: String): R = runBlocking { httpClient.prepareGet(url).body() }

private fun <R> withEmbeddedServer(
moduleFunction: Application.() -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@ import com.auth0.jwt.interfaces.Payload
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.auth.Authentication
import io.ktor.auth.authenticate
import io.ktor.auth.jwt.JWTCredential
import io.ktor.auth.jwt.JWTPrincipal
import io.ktor.auth.jwt.jwt
import io.ktor.auth.principal
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.features.json.JacksonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.get
import io.ktor.response.respond
import io.ktor.routing.get
import io.ktor.routing.routing
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.prepareGet
import io.ktor.serialization.jackson.jackson
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.auth.Authentication
import io.ktor.server.auth.authenticate
import io.ktor.server.auth.jwt.JWTCredential
import io.ktor.server.auth.jwt.JWTPrincipal
import io.ktor.server.auth.jwt.jwt
import io.ktor.server.auth.principal
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import java.net.URL
Expand Down Expand Up @@ -99,15 +99,15 @@ class AuthConfig(
val requiredClaims: Map<String, Any> = emptyMap()
) {
private val httpClient = HttpClient(CIO) {
install(JsonFeature) {
serializer = JacksonSerializer {
install(ContentNegotiation) {
jackson {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
}
}

val wellKnown: WellKnown = runBlocking { httpClient.get(wellKnownUrl) }
val wellKnown: WellKnown = runBlocking { httpClient.prepareGet(wellKnownUrl).body() }
val jwkProvider = JwkProviderBuilder(URL(wellKnown.jwksUri))
.cached(10, 24, TimeUnit.HOURS)
.rateLimited(10, 1, TimeUnit.MINUTES)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package examples.kotlin.ktor.resourceserver

import io.kotest.matchers.shouldBe
import io.ktor.http.HttpMethod
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.handleRequest
import io.ktor.server.testing.withTestApplication
import io.ktor.server.testing.testApplication
import no.nav.security.mock.oauth2.MockOAuth2Server
import no.nav.security.mock.oauth2.withMockOAuth2Server
import org.junit.jupiter.api.Test
Expand All @@ -15,14 +16,13 @@ class OAuth2ResourceServerAppTest {
fun `http get to secured endpoint without token should return 401`() {
withMockOAuth2Server {
val authConfig = authConfig()
withTestApplication({
module(authConfig)
}) {
with(
handleRequest(HttpMethod.Get, "/hello1")
) {
response.status() shouldBe HttpStatusCode.Unauthorized

testApplication {
this.application {
module(authConfig)
}
val response = client.get("/hello1")
response.status shouldBe HttpStatusCode.Unauthorized
}
}
}
Expand All @@ -32,17 +32,16 @@ class OAuth2ResourceServerAppTest {
withMockOAuth2Server {
val mockOAuth2Server = this
val authConfig = authConfig()
withTestApplication({
module(authConfig)
}) {
with(
handleRequest(HttpMethod.Get, "/hello1") {
addHeader("Authorization", "Bearer ${mockOAuth2Server.tokenFromProvider1()}")
}
) {
response.status() shouldBe HttpStatusCode.OK
response.content shouldBe "hello1 foo from issuer ${mockOAuth2Server.issuerUrl("provider1")}"
testApplication {
this.application {
module(authConfig)
}
val response = client.get("/hello1"){
header("Authorization", "Bearer ${mockOAuth2Server.tokenFromProvider1()}")
}

response.status shouldBe HttpStatusCode.OK
response.bodyAsText() shouldBe "hello1 foo from issuer ${mockOAuth2Server.issuerUrl("provider1")}"
}
}
}
Expand All @@ -52,17 +51,16 @@ class OAuth2ResourceServerAppTest {
withMockOAuth2Server {
val mockOAuth2Server = this
val authConfig = authConfig()
withTestApplication({
module(authConfig)
}) {
with(
handleRequest(HttpMethod.Get, "/hello2") {
addHeader("Authorization", "Bearer ${mockOAuth2Server.tokenFromProvider2()}")
}
) {
response.status() shouldBe HttpStatusCode.OK
response.content shouldBe "hello2 foo from issuer ${mockOAuth2Server.issuerUrl("provider2")}"
testApplication {
this.application {
module(authConfig)
}

val response = client.get("/hello2"){
header("Authorization", "Bearer ${mockOAuth2Server.tokenFromProvider2()}")
}
response.status shouldBe HttpStatusCode.OK
response.bodyAsText() shouldBe "hello2 foo from issuer ${mockOAuth2Server.issuerUrl("provider2")}"
}
}
}
Expand Down