How to authenticate with web form in Ktor?


#1

I could not find a working example of authenticating with web form. Here is my setup.

install(Authentication) {
        form("myFormAuth") {
            userParamName = "username"
            passwordParamName = "password"
            challenge = FormAuthChallenge.Redirect{ "/login" }
            validate { if (it.name == "test" && it.password == "password") UserIdPrincipal(it.name) else null }
        }
}

Then I can secure a route like this

authenticate("myFormAuth") {
        get("/protected/route/form") {
            call.respondText("Hello form auth")
        }
}

But how can I actually authenticate a user? What does a [POST] route/action that handle authentication look like? Should it set some cookie session?

Here is an example of using form authentication, but (i) it does not install/register an authentication mechanism, and (ii) we don’t see how it would handle a successful and failed authenticating attempt.

Could someone provide a better example?

Thanks.


#2

Pretty sad that such a question receives no replies.

Anyway, I think I found the solution in case someone has the same problem.

The idea is that authentication mechanism in Ktor is responsible for validating a login request, so a route encapsulated by authenticate(“my-auth-name”) will also attempt to find credential data (username/password) in the request. It does not do anything special, like marking authenticate status in session. It is up to us to do that. So I updated my code with the following 2 steps:

  1. In my post("/login") route, set a user session upon successful authentication, i.e. principal is not null.
data class AuthenticatedUser(val name: String)

//...//

authenticate("myFormAuth") {
        post("/login") {
              val principal = call.principal<UserIdPrincipal>() ?: error ("no auth found")
              call.sessions.set(AuthenticatedUser(principal.name))
        }

        get("/protected/route/form") {
            call.respondText("Hello form auth")
        }
}
  1. In the authentication mechanism, skip authentication when such a session is present.
install(Authentication) {
        form("myFormAuth") {
            userParamName = "username"
            passwordParamName = "password"
            challenge = FormAuthChallenge.Redirect{ "/login" }
            validate { if (it.name == "test" && it.password == "password") UserIdPrincipal(it.name) else null }
            skipWhen { call -> call.sessions.get<AuthenticatedUser> != null }
        }
}