'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 1:[1]

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"
}

Solution 2:[2]

For Ktor 2.x the solution proposed by Alexsei does not work anymore because ChallengePhase is now marked as internal as they completely restructured the plugin system.

This code snippet seems to be working for me.

fun Route.authorization(build: Route.() -> Unit): Route {
    val route = createChild(CustomSelector())
    val plugin = createRouteScopedPlugin("CustomAuthorization") {
        on(AuthenticationChecked) { call ->
            val principal = call.authentication.principal
            // custom logic
        }
    }
    route.install(plugin)
    route.build()
    return route
}

private class CustomSelector : RouteSelector() {
    override fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent
}

Of course, you can add parameters to the function specifying the restriction like required roles.

To secure a route...

fun Route.myRoute() = route("test") {
    authorization {
        get { ... }
    }
}

For more details: https://ktor.io/docs/custom-plugins.html

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aleksei Tirman
Solution 2 Intex32