Search code examples
kotlinktor

How to restrict route access in ktor framework?


How to restrict route access in ktor framework?

//only admin           
post("/add") {
   call.respondText { "add" }
}

post("/delete") {
   call.respondText { "delete" }
}
        

Solution

  • You can write a method that creates a route that restricts access for admins only. Inside that method, the newly created route is intercepted to inject the code for validation. In the following example, if the header admin has the value 1 then a request is made from an admin otherwise for the /add and /delete routes the response with the 401 Unauthorized status will be returned.

    import io.ktor.application.*
    import io.ktor.auth.*
    import io.ktor.http.*
    import io.ktor.request.*
    import io.ktor.response.*
    import io.ktor.routing.*
    import io.ktor.server.engine.*
    import io.ktor.server.netty.*
    import io.ktor.util.pipeline.*
    
    fun main() {
        embeddedServer(Netty, port = 5555, host = "0.0.0.0") {
            routing {
                admin {
                    post("/add") {
                        call.respondText { "add" }
                    }
    
                    post("/delete") {
                        call.respondText { "delete" }
                    }
                }
    
                post("/show") {
                    call.respondText { "show" }
                }
            }
        }.start(wait = false)
    }
    
    private val validationPhase = PipelinePhase("Validate")
    
    fun Route.admin(build: Route.() -> Unit): Route {
        val route = createChild(AdminSelector())
    
        route.insertPhaseAfter(ApplicationCallPipeline.Features, Authentication.ChallengePhase)
        route.insertPhaseAfter(Authentication.ChallengePhase, validationPhase)
        route.intercept(validationPhase) {
            if (!isAdmin(call.request)) {
                call.respond(HttpStatusCode.Forbidden)
                finish()
            }
        }
        route.build()
        return route
    }
    
    class AdminSelector: RouteSelector() {
        override fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent
    }
    
    fun isAdmin(request: ApplicationRequest): Boolean {
        return request.headers["admin"] == "1"
    }