https://kotlinlang.org logo
Title
b

Bendik Standal

03/31/2023, 1:18 PM
Hi everyone! I just discovered this wonderful community and now I have a question I hope someone can help me with. I am currently developing a Ktor application serving some endpoints to interact with a database. The endpoints defined in the routing block all exist in an authenticated block, so all requests have to include a JWT using the Bearer authentication scheme. Authentication is configured to accept JWTs signed with the RSA algorithm, very similar to the instructions in the Ktor documentation https://ktor.io/docs/jwt.html . The application works as expected, but I have a problem when creating integration tests for this application, as I then have to generate my own JWT for these tests and then somehow host a .well-known/jwks.json file for the JwkProvider to read the signing key from. From the ktor server testing documentation, I thought it would be possible to add a route in a
testApplication
hosting a jwks.json file with something like this:
@Test
fun `test that cases are correctly added to database`() = testApplication {
	routing {
		static(".well-known") {
			staticRootFolder = File("certs")
			file("jwks.json")
		}
	}
	
	<...>
}
However, that does not seem to work, and my theory is that the application sets up the routing defined here after the authentication is installed and configured(?). So my question is, does someone know a way of setting up this route before the authentication in the application is configured? Or alternatively, if someone has experience in writing integration tests for apis that are secured with (RSA signed) JSON web tokens, perhaps you can point me to a better solution for solving this. Hopefully that made sense to someone :-)
Authentication config:
val jwksEndpoint = environment.config.config("jwt").property("keyDomain").getString()
val jwkProvider = JwkProviderBuilder(URL(jwksEndpoint))
    .cached(10, 24, TimeUnit.HOURS)
    .rateLimited(10, 1, TimeUnit.MINUTES)
    .build()
install(Authentication) {
    jwt("auth-jwt") {
        verifier(jwkProvider, issuer) {
            acceptLeeway(10)
        }
        validate { credential ->
            if (credential.payload.audience.contains(audience)) JWTPrincipal(credential.payload) else null
        }
    }
a

Aleksei Tirman [JB]

04/04/2023, 4:33 AM
The solution with a serving static content won’t work because the
UrlJwkProvider
uses a HTTP client which doesn’t know anything about the test environment. I suggest defining a parameter with the
JwkProvider
type for the module function to be able to pass a fake provider in tests. You can find a sample test in this PR.
b

Bendik Standal

04/17/2023, 10:45 AM
Ah, that makes sense. Thanks!