# How to authenticate with web form in Ktor?

**URL:** <https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227>\
**Category:** Web Development\
**Created:** [January 18, 2019, 11:18am UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227 "2019-01-18T11:18:37Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![kha](https://avatars.discourse-cdn.com/v4/letter/k/9de053/32.png) [@kha](https://discuss.kotlinlang.org/u/kha)\
**Post date:** [January 18, 2019, 11:18am UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/1 "2019-01-18T11:18:37Z")

</div>

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](https://github.com/ktorio/ktor-samples/blob/master/feature/auth/src/io/ktor/samples/auth/FormPostApplication.kt) 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.

---

<div class="post-metadata">

**Author:** ![kha](https://avatars.discourse-cdn.com/v4/letter/k/9de053/32.png) [@kha](https://discuss.kotlinlang.org/u/kha)\
**Post date:** [January 20, 2019, 12:43am UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/2 "2019-01-20T00:43:50Z")

</div>

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.

```kotlin
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.

```kotlin
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 }
        }
}

```

---

<div class="post-metadata">

**Author:** ![nanodeath](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/nanodeath/32/1803_2.png) [@nanodeath](https://discuss.kotlinlang.org/u/nanodeath)\
**Post date:** [August 25, 2019, 5:45pm UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/3 "2019-08-25T17:45:25Z")

</div>

Hi @kha,

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](https://github.com/ktorio/ktor/blob/19054d036f6ee9dc89ae982c96efe900221ae39b/ktor-features/ktor-auth/jvm/src/io/ktor/auth/Authentication.kt#L115-L116): `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`](https://github.com/ktorio/ktor/blob/19054d036f6ee9dc89ae982c96efe900221ae39b/ktor-features/ktor-auth/jvm/src/io/ktor/auth/Authentication.kt#L162-L165) 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:

```auto
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(...))`.

---

<div class="post-metadata">

**Author:** ![nanodeath](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/nanodeath/32/1803_2.png) [@nanodeath](https://discuss.kotlinlang.org/u/nanodeath)\
**Post date:** [August 25, 2019, 6:10pm UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/4 "2019-08-25T18:10:07Z")

</div>

And…one more thing. How do you handle failed attempts by the user to authenticate? e.g. when do you display “invalid username or password”?

In your Authentication `form` configuration block:

```kotlin
challenge {
    val errors: Map<Any, AuthenticationFailedCause> = call.authentication.errors
    when (errors.values.singleOrNull()) {
        AuthenticationFailedCause.InvalidCredentials ->
            call.respondRedirect("/auth?invalid")

        AuthenticationFailedCause.NoCredentials ->
            call.respondRedirect("/auth")

        else ->
            call.respondRedirect("/auth")
    }
}

```

Then you can check the query parameter in your route.

(In case it’s not obvious, `NoCredentials` is the branch that’s hit when you try to hit a guarded page without even trying to authenticate first.)

---

<div class="post-metadata">

**Author:** ![nanodeath](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/nanodeath/32/1803_2.png) [@nanodeath](https://discuss.kotlinlang.org/u/nanodeath)\
**Post date:** [August 27, 2019, 8:50pm UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/5 "2019-08-27T20:50:11Z")

</div>

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](https://gitlab.com/nanodeath/ktor-session-auth-example). Check it out and let me know what you think!

---

<div class="post-metadata">

**Author:** ![doronantonin](https://avatars.discourse-cdn.com/v4/letter/d/4af34b/32.png) [@doronantonin](https://discuss.kotlinlang.org/u/doronantonin)\
**Post date:** [April 24, 2020, 4:59pm UTC](https://discuss.kotlinlang.org/t/how-to-authenticate-with-web-form-in-ktor/11227/6 "2020-04-24T16:59:33Z")

</div>

Thanks a lot for your clarifying examples.
