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.
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:
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")
}
}
In the authentication mechanism, skip authentication when such a session is present.
I’m assuming you’ve moved on from this by now, but for others (because this was a top search result), I’m going to document a more correct solution, because it’s a bit confusing.
The Authentication module that ktor provides generates two phases: Authenticate and Challenge, inserted in that order after the Features phase. These phases are executed every time a route inside a authenticate {} block is hit. Authenticate means “let’s try to authenticate the user based on the request” and Challenge means “try again at the login page”, essentially. (Naturally Authenticate doesn’t do much if you’re already authenticated)
The point is…that Authenticate phase? Only gets hit from inside an authenticate {} route!
tl:dr; something like this should work:
get("/auth") {
call.respondHtml {
body {
form(action = "/auth", method = FormMethod.post) {
// a form using userParamName and passwordParamName
// from when you configured form auth
}
}
}
}
authenticate {
post("/auth") {
call.respondRedirect("/user_is_signed_in")
}
}
Alternatively, if you have a signed-in-only landing page, you can configure your form to POST directly to that URL; the important thing is that where you POST to must be authenticated.
Bonus question…when registering, how do you auto-sign in the user afterwards?
I think the answer is call.authentication.principal(UserIdPrincipal(...)).
Alright, I’ve put together a full sample application that uses cookies and ktor’s auth mechanisms, which you can find here: ktor-session-auth-example. Check it out and let me know what you think!