124 lines
3.7 KiB
Kotlin
124 lines
3.7 KiB
Kotlin
package be.vandewalleh.routing
|
|
|
|
import be.vandewalleh.auth.SimpleJWT
|
|
import be.vandewalleh.auth.UsernamePasswordCredential
|
|
import be.vandewalleh.extensions.RoutingBuilder
|
|
import be.vandewalleh.extensions.authenticatedUserId
|
|
import be.vandewalleh.extensions.respondStatus
|
|
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.*
|
|
|
|
class UserRoutes(
|
|
authJWT: SimpleJWT,
|
|
refreshJWT: SimpleJWT,
|
|
userService: UserService,
|
|
passwordHash: PasswordHash
|
|
) : RoutingBuilder({
|
|
route("/user") {
|
|
createUser(userService)
|
|
route("/login") {
|
|
login(userService, passwordHash, authJWT, refreshJWT)
|
|
}
|
|
route("/refresh_token") {
|
|
refreshToken(userService, authJWT, refreshJWT)
|
|
}
|
|
authenticate {
|
|
deleteUser(userService)
|
|
route("/me") {
|
|
userInfo(userService)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
private fun Route.userInfo(userService: UserService) {
|
|
get {
|
|
val id = call.authenticatedUserId()
|
|
val info = userService.find(id)
|
|
if (info != null) call.respond(mapOf("user" to info))
|
|
else call.respondStatus(HttpStatusCode.Unauthorized)
|
|
}
|
|
}
|
|
|
|
private fun Route.deleteUser(userService: UserService) {
|
|
delete {
|
|
val userId = call.authenticatedUserId()
|
|
call.respondStatus(
|
|
if (userService.delete(userId)) HttpStatusCode.OK
|
|
else HttpStatusCode.NotFound
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun Route.createUser(userService: UserService) {
|
|
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)
|
|
}
|
|
}
|
|
|
|
private fun Route.login(
|
|
userService: UserService,
|
|
passwordHash: PasswordHash,
|
|
authJWT: SimpleJWT,
|
|
refreshJWT: SimpleJWT
|
|
) {
|
|
post {
|
|
val credential = call.receive<UsernamePasswordCredential>()
|
|
|
|
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 = authJWT.sign(user.id),
|
|
refreshToken = refreshJWT.sign(user.id)
|
|
)
|
|
return@post call.respond(response)
|
|
}
|
|
}
|
|
|
|
private fun Route.refreshToken(userService: UserService, authJWT: SimpleJWT, refreshJWT: SimpleJWT) {
|
|
post {
|
|
val token = call.receive<RefreshToken>().refreshToken
|
|
|
|
val id = try {
|
|
val decodedJWT = refreshJWT.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 = authJWT.sign(id),
|
|
refreshToken = refreshJWT.sign(id)
|
|
)
|
|
return@post call.respond(response)
|
|
}
|
|
}
|
|
|
|
private data class RefreshToken(val refreshToken: String)
|
|
private data class DualToken(val token: String, val refreshToken: String)
|