Ktor-OpenAPI-Generator: Security documentation not being generated

We’ve been able to implement JWT-authenticated endpoints through the Ktor-OpenAPI-Generator API, and authentication works - but the generated OpenAPI.json doesn’t contain any information about the security scheme. It should, right?

We have written the following code to tell Ktor-OpenAPI-Generator about the authentication:

  val authProvider = JwtProvider();

  inline fun NormalOpenAPIRoute.auth(route: OpenAPIAuthenticatedRoute<UserPrincipal>.() -> Unit): 
  OpenAPIAuthenticatedRoute<UserPrincipal> {
    val authenticatedKtorRoute = this.ktorRoute.authenticate { }
    var openAPIAuthenticatedRoute= OpenAPIAuthenticatedRoute(authenticatedKtorRoute, this.provider.child(), authProvider = authProvider);
    return openAPIAuthenticatedRoute.apply {
        route()
    }
  }

  data class UserPrincipal(val userId: String, val email: String?, val name: String?) : Principal

  class JwtProvider : AuthProvider<UserPrincipal> {
    override val security: Iterable<Iterable<Security<*>>> =
        listOf(listOf(Security(SecuritySchemeModel(SecuritySchemeType.openIdConnect, scheme = HttpSecurityScheme.bearer, bearerFormat = "JWT", name = "JWT"), Scopes.values().toList())))

    override suspend fun getAuth(pipeline: PipelineContext<Unit, ApplicationCall>): UserPrincipal {
        return pipeline.context.authentication.principal() ?: throw RuntimeException("No JWTPrincipal")
    }

    override fun apply(route: NormalOpenAPIRoute): OpenAPIAuthenticatedRoute<UserPrincipal> {
        val authenticatedKtorRoute = route.ktorRoute.authenticate { }
        return OpenAPIAuthenticatedRoute(authenticatedKtorRoute, route.provider.child(), this)
    }
  }

  enum class Scopes(override val description: String) : Described {
    Profile("Some scope")
  }

In our Application.apirouting, we wrap the required paths with auth{}, and make sure we use the routing functions from com.papsign.ktor.openapigen.route.path.auth.* in those cases. We are able to access the authenticated user through the principal()-function. An example:

auth {
  route("/user") {
        get<Unit, List<User>, UserPrincipal> {
            val principal = principal()
            log.info("principal1 ${principal.userId}")
            log.info("principal1 ${principal.email}")
            respond(registry.userRepository.retrieveAllUsers())
        }
  }
}

Everything seems to work fine for actual use - but as mentioned, we’re not finding any information about the security scheme in the generated openapi.json. 😦

We are calling addModules(authProvider) as part of the installation of OpenAPIGen, but we’re under the impression this isn’t working as intended. (We are supposed to add it as a module, right?) Here, we’re ending up in the object com.papsign.ktor.openapigen.modules.handlers.AuthHandler:

package com.papsign.ktor.openapigen.modules.handlers

import com.papsign.ktor.openapigen.OpenAPIGen
import com.papsign.ktor.openapigen.model.operation.OperationModel
import com.papsign.ktor.openapigen.model.security.SecurityModel
import com.papsign.ktor.openapigen.modules.ModuleProvider
import com.papsign.ktor.openapigen.modules.ofType
import com.papsign.ktor.openapigen.modules.openapi.OperationModule
import com.papsign.ktor.openapigen.modules.providers.AuthProvider

object AuthHandler: OperationModule {
    override fun configure(apiGen: OpenAPIGen, provider: ModuleProvider<*>, operation: OperationModel) {
        val authHandlers = provider.ofType<AuthProvider<*>>()
        val security = authHandlers.flatMap { it.security }.distinct()
        operation.security = security.map { SecurityModel().also { sec ->
            it.forEach { sec[it.scheme.name] = it.requirements }
        } }
        apiGen.api.components.securitySchemes.putAll(security.flatMap { it.map { it.scheme } }.associateBy { it.name })
    }
}

Here, in the configure method, provider seems to contain our authProvider object, but it isn’t returned from the “provider.ofType<AuthProvider<*>>()” call. But at this point we’re a bit uncertain if we’re barking up the wrong tree, and if we’re not, why our AuthProvider isn’t recognized as one by ofType…

We’re not sure if this is a bug in OpenAPIGen or just something we haven’t understood about how to use the framework. Any help would be greatly appreciated.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 19 (3 by maintainers)

Most upvoted comments

Actually I’m mistaken, my latest KType changes have not been released yet - but I will take a look at it regardless as I have the same problem.

Hello, I actually wrote that change to KType and must have missed something. I will take a look at it shortly (as I noticed this problem myself with our my project). A failing test would be helpful.