https://kotlinlang.org logo
#ktor
Title
# ktor
d

Damian Lall

09/09/2019, 2:22 PM
I'm not sure if this is intended behavior, but I'm having trouble with basic authentication and route interception with Ktor. When I set up a basic authentication provider and put a simple
get
handler inside an
authenticate
block, I'll be prompted for credentials when I try to access the route, so once the
get
handler starts being executed, I can access the
UserIdPrincipal
and start finding data associated with the account. However, I now want to expand my
authenticate
block to include multiple routes, so I thought I could handle the initial processing of the principal/account inside an
intercept
block. Yet, when I try to do this, I am not prompted for my credentials and therefore the
UserIdPrincipal
inside the interceptor is null. How can I get Ktor to prompt me for my credentials from within a route interceptor inside an
authenticate
block?
This code properly prompts me for my credentials when I try to access the dashboard route.
Copy code
routing {
    authenticate("teacherAuth") {
        get("dashboard") {
            val principal = call.principal<UserIdPrincipal>()!!
            val schoolName = principal.name
            val school = transaction {
                School.find { Schools.name eq schoolName }.singleOrNull()
            }
            if (school == null)
                call.respondText("No school \"$schoolName\" found")
            else
                call.respondHtml {
                    ...
                }
        }
    }
}
This code does not prompt me for my credentials when I try to access the dashboard route.
Copy code
authenticate("teacherAuth") {
    val schoolKey = AttributeKey<School>("school")

    intercept(ApplicationCallPipeline.Setup) {
        val principal = call.principal<UserIdPrincipal>()!!
        val schoolName = principal.name
        val school = transaction {
            School.find { Schools.name eq schoolName }.singleOrNull()
        }
        if (school == null) {
            call.respondText("No school \"$schoolName\" found")
            return@intercept finish()
        }
        call.attributes.put(schoolKey, school)
    }

    get("dashboard") {
        val school = call.attributes[schoolKey]
        call.respondHtml {
            ...
        }
    }
}
s

Sergey Akhapkin

09/10/2019, 8:17 AM
If your client is a browser, it may cache your credentials for follow-up requests to same domain: https://tools.ietf.org/html/rfc7617#section-2.2
d

Damian Lall

09/10/2019, 2:39 PM
For my application, I will likely have people entering large amounts of data over a reasonably long period of time. Am I supposed to let it authenticate once when visiting the dashboard and let it cache the credentials for the remaining requests? What happens if the cache expires sometime after while someone is still working?
s

Sergey Akhapkin

09/10/2019, 7:45 PM
I think it depends on a browser. From my experience, e.g. Chrome never ask to re-enter credentials while tab is open. Anyway, if you want more controllable or/and customize you need to use your own authentication.