SimpleNotes/src/controllers/api/ApiUserController.kt

88 lines
3.1 KiB
Kotlin

package be.vandewalleh.controllers.api
import be.vandewalleh.auth.SimpleJWT
import be.vandewalleh.auth.UsernamePasswordCredential
import be.vandewalleh.extensions.authenticatedUser
import be.vandewalleh.features.PasswordHash
import be.vandewalleh.repositories.UserRepository
import be.vandewalleh.validation.receiveValidated
import be.vandewalleh.validation.registerValidator
import com.auth0.jwt.exceptions.JWTVerificationException
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
class ApiUserController(
private val authJWT: SimpleJWT,
private val refreshJWT: SimpleJWT,
private val userRepository: UserRepository,
private val passwordHash: PasswordHash
) {
suspend fun create(call: ApplicationCall) {
val user = call.receiveValidated(registerValidator)
if (userRepository.exists(user.username))
return call.response.status(HttpStatusCode.Conflict)
userRepository.create(user.username, user.password)
?: return call.response.status(HttpStatusCode.Conflict)
call.response.status(HttpStatusCode.Created)
}
suspend fun login(call: ApplicationCall) {
val credential = call.receive<UsernamePasswordCredential>()
val user = userRepository.find(credential.username)
?: return call.response.status(HttpStatusCode.Unauthorized)
if (!passwordHash.verify(credential.password, user.password)) {
return call.response.status(HttpStatusCode.Unauthorized)
}
val response = DualToken(
token = authJWT.sign(user.id, user.username),
refreshToken = refreshJWT.sign(user.id, user.username)
)
return call.respond(response)
}
suspend fun refreshToken(call: ApplicationCall) {
val token = call.receive<RefreshToken>().refreshToken
val id = try {
val decodedJWT = refreshJWT.verifier.verify(token)
decodedJWT.getClaim("id").asInt()
} catch (e: JWTVerificationException) {
return call.response.status(HttpStatusCode.Unauthorized)
}
val user = userRepository.find(id) ?: return call.response.status(HttpStatusCode.Unauthorized)
val response = DualToken(
token = authJWT.sign(user.id, user.username),
refreshToken = refreshJWT.sign(user.id, user.username)
)
return call.respond(response)
}
suspend fun delete(call: ApplicationCall) {
val userId = call.authenticatedUser().id
val success = userRepository.delete(userId)
if (success) call.response.status(HttpStatusCode.OK)
else call.response.status(HttpStatusCode.NotFound)
}
suspend fun info(call: ApplicationCall) {
val id = call.authenticatedUser().id
val info = userRepository.find(id)
if (info != null) call.respond(mapOf("user" to info))
else call.response.status(HttpStatusCode.Unauthorized)
}
}
private data class RefreshToken(val refreshToken: String)
private data class DualToken(val token: String, val refreshToken: String)