package be.vandewalleh.routing import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.auth.UserDbIdPrincipal import be.vandewalleh.auth.UsernamePasswordCredential import be.vandewalleh.extensions.respondStatus import be.vandewalleh.extensions.authenticatedUserId import be.vandewalleh.features.PasswordHash import be.vandewalleh.services.UserService import be.vandewalleh.validation.receiveValidated import be.vandewalleh.validation.registerValidator import com.auth0.jwt.exceptions.JWTVerificationException import io.ktor.application.* import io.ktor.auth.* import io.ktor.http.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* import org.kodein.di.Kodein import org.kodein.di.generic.instance data class RefreshToken(val refreshToken: String) data class DualToken(val token: String, val refreshToken: String) fun Route.userRoutes(kodein: Kodein) { val authSimpleJwt by kodein.instance("auth") val refreshSimpleJwt by kodein.instance("refresh") val userService by kodein.instance() val passwordHash by kodein.instance() post { val user = call.receiveValidated(registerValidator) if (userService.exists(user.username)) return@post call.respondStatus(HttpStatusCode.Conflict) val newUser = userService.create(user.username, user.password) ?: return@post call.respondStatus(HttpStatusCode.Conflict) call.respond(HttpStatusCode.Created, newUser) } post("/login") { val credential = call.receive() val user = userService.find(credential.username) ?: return@post call.respondStatus(HttpStatusCode.Unauthorized) if (!passwordHash.verify(credential.password, user.password)) { return@post call.respondStatus(HttpStatusCode.Unauthorized) } val response = DualToken( token = authSimpleJwt.sign(user.id), refreshToken = refreshSimpleJwt.sign(user.id) ) return@post call.respond(response) } post("/refresh_token") { val token = call.receive().refreshToken val id = try { val decodedJWT = refreshSimpleJwt.verifier.verify(token) decodedJWT.getClaim("id").asInt() } catch (e: JWTVerificationException) { return@post call.respondStatus(HttpStatusCode.Unauthorized) } if (!userService.exists(id)) return@post call.respondStatus(HttpStatusCode.Unauthorized) val response = DualToken( token = authSimpleJwt.sign(id), refreshToken = refreshSimpleJwt.sign(id) ) return@post call.respond(response) } authenticate { delete { val userId = call.authenticatedUserId() call.respondStatus( if (userService.delete(userId)) HttpStatusCode.OK else HttpStatusCode.NotFound ) } get("/me") { val id = call.principal()!!.id val info = userService.find(id) if (info != null) call.respond(mapOf("user" to info)) else call.respondStatus(HttpStatusCode.Unauthorized) } } }