papsign / Ktor-OpenAPI-Generator

Ktor OpenAPI/Swagger 3 Generator
Apache License 2.0
241 stars 42 forks source link

Custom auth on route #34

Closed mintu19 closed 4 years ago

mintu19 commented 4 years ago

I am trying to do custom token based authentication on whole route but using ktor authentication is not working. How to add authentication method for openapi?

fun Routing.userRoute() {
    apiRouting {
        authenticate {
            route("users", Tags.USER) {
                route("agent").get<Unit, UserAgentResponse>(
                    info("Get Agent", "Get agent used to get images from server"),
                    example = UserAgentResponse("qwerty")
                ) {
                    respond(UserAgentResponse(Helper.userAgent))
                }
            }
        }
    }
}
Wicpar commented 4 years ago

It is not possible to get Ktor information on the authentication, you would need to implement the interface yourself to provide the information, here are previous related issues for details: OAuth: #30 JWT: #8

mintu19 commented 4 years ago

I have checked above issues but not able to understand how to implement it for my custom auth provider (it is like digest) and how to use it with routes inside apiRouting. That is to use it with all routes individually or can use it like above?

Wicpar commented 4 years ago

To use it like you want up there you would:

  1. Implement the auth handler:

    private lass CustomAuthProvider(scopes: List<T>) : AuthProvider<A> {
        override suspend fun getAuth(pipeline: PipelineContext<Unit, ApplicationCall>): A {
             // return the object that represents an authenticated user, like the ktor authentication()
        }
    
        // use the ktor authentication on the route, the throw is optional but recommended if you want to handle errors properly
        override fun apply(route: NormalOpenAPIRoute): OpenAPIAuthenticatedRoute<A> =
            OpenAPIAuthenticatedRoute(route.ktorRoute.authenticate(authName) {}, route.provider.child(), this).throws(
                APIException.apiException<BadPrincipalException>(HttpStatusCode.Unauthorized)
            )
        // tell OpenAPI how to generate the security definition you implemented
        override val security: Iterable<Iterable<AuthProvider.Security<*>>> =
            listOf(listOf(AuthProvider.Security(scheme, scopes)))
    }
  2. Define a helper function to implement it on your route
    inline fun<T> NormalOpenAPIRoute.auth(provider: AuthProvider<T>, crossinline route: OpenAPIAuthenticatedRoute<T>.()->Unit = {}): OpenAPIAuthenticatedRoute<T> {
    return provider.apply(this).apply {
        route()
    }
    }
  3. use it
    
    val authProvider = CustomAuthProvider() // you could also have the authProvider as an object instead

fun Routing.userRoute() { apiRouting { auth(authProvider) { // use it wherever you have a non authenticated route to create an authenticated one route("users", Tags.USER) { route("agent").get<Unit, UserAgentResponse>( info("Get Agent", "Get agent used to get images from server"), example = UserAgentResponse("qwerty") ) { respond(UserAgentResponse(Helper.userAgent)) } } } } }

mintu19 commented 4 years ago

It worked. Testing it now.

AndreyZS commented 3 years ago

Good afternoon, you can please a more detailed example of implementation, unfortunately I do not understand how to implement "getAuth" and "security" for jwt ?