Reputation: 63
I'm using ktor v0.9.2 and I want to send diferent content for the same route, based if the user is authenticated or not.
The problem I'm having is that I'm not being able to access the principal outside the authenticate { }
block.
My setup is like this:
data class User(
val userId: Int
) : io.ktor.auth.Principal
fun Application.myApp() {
install(Authentication) {
jwt {
verifier(JwtConfig.verifier)
validate { credential ->
val userId = credential.payload.getClaim("userId").asInt()
when {
userId > 0 -> User(userId)
else -> null
}
}
}
}
install(DefaultHeaders)
install(CallLogging)
install(ContentNegotiation) {
jackson { }
}
install(Routing) {
authenticate {
get("/protected") {
// This works fine!!
val user = call.authentication.principal<User>()
call.respond(user)
}
}
get("/") {
val user = call.authentication.principal<User>() // -> User is always null here
if (user == null) {
call.respondText("Not Logged User")
} else {
call.respondText("Logged User")
}
}
}
}
The /protected
route works fine, but in the /
route the principal is always null. I thinks this is some pipeline thing, but I am a little lost. Could someone give some insights? Thanks!
Upvotes: 4
Views: 5672
Reputation: 3105
What version of ktor are you using? Can you show us your auth setup?
You should have something like this (0.9.2):
install(Authentication) {
jwt {
verifier(JwtConfig.verifier)
realm = JwtConfig.realm
validate {
val email = it.payload.getClaim("email").toString()
userRepository.findUser(email)?.let { user ->
val token = JwtConfig.makeToken(user)
user.copy(token = token)
}
}
}
}
If the auth process is successful, the user will be available via principal.
Here is the updated code for 0.9.3.
Starting off with a test to verify behavior.
Simply add the optional
flag.
class KtorTest {
@Test fun server() {
withTestApplication({ myApp() }) {
val userId = 918354853
val token = JwtConfig.makeToken(User(userId))
// The protected route
handleRequest {
uri = "/protected"
addHeader("Authorization", "Bearer $token")
}.let {
it.requestHandled shouldBe true
it.response.content.shouldNotBeNullOrBlank() shouldContain userId.toString()
}
// Optional route without token
handleRequest {}.let {
it.requestHandled shouldBe true
it.response.content.shouldNotBeNullOrBlank() shouldBeEqualTo "Not Logged User"
}
// Optional route with token
handleRequest {
addHeader("Authorization", "Bearer $token")
}.let {
it.requestHandled shouldBe true
it.response.content.shouldNotBeNullOrBlank() shouldBeEqualTo "Logged User"
}
}
}
}
data class User(val userId: Int) : Principal
fun Application.myApp() {
install(Authentication) {
jwt {
verifier(JwtConfig.verifier)
validate { credential ->
val userId = credential.payload.getClaim("userId").asInt()
when {
userId > 0 -> User(userId)
else -> null
}
}
}
}
install(DefaultHeaders)
install(CallLogging)
install(ContentNegotiation) { jackson { } }
install(Routing) {
authenticate {
get("/protected") {
// This works fine!!
val user = call.authentication.principal<User>()!!
call.respond(user)
}
}
authenticate(optional = true) {
get("/") {
val user = call.authentication.principal<User>() // -> User is always null here
if (user == null) {
call.respondText("Not Logged User")
} else {
call.respondText("Logged User")
}
}
}
}
}
Upvotes: 5