mirror of https://github.com/usbharu/Hideout.git
refactor: JWT関係のコードをリファクタリング
This commit is contained in:
parent
8640fc44ee
commit
95e47a0e9b
|
@ -86,7 +86,9 @@ fun Application.parent() {
|
||||||
single<IdGenerateService> { TwitterSnowflakeIdGenerateService }
|
single<IdGenerateService> { TwitterSnowflakeIdGenerateService }
|
||||||
single<IMetaRepository> { MetaRepositoryImpl(get()) }
|
single<IMetaRepository> { MetaRepositoryImpl(get()) }
|
||||||
single<IServerInitialiseService> { ServerInitialiseServiceImpl(get()) }
|
single<IServerInitialiseService> { ServerInitialiseServiceImpl(get()) }
|
||||||
single<IJwtRefreshTokenRepository> { JwtRefreshTokenRepositoryImpl(get()) }
|
single<IJwtRefreshTokenRepository> { JwtRefreshTokenRepositoryImpl(get(),get()) }
|
||||||
|
single<IMetaService> { MetaServiceImpl(get()) }
|
||||||
|
single<IJwtService> { JwtServiceImpl(get(),get(),get()) }
|
||||||
}
|
}
|
||||||
configureKoin(module)
|
configureKoin(module)
|
||||||
runBlocking {
|
runBlocking {
|
||||||
|
@ -99,10 +101,9 @@ fun Application.parent() {
|
||||||
register(inject<IUserService>().value)
|
register(inject<IUserService>().value)
|
||||||
configureSecurity(
|
configureSecurity(
|
||||||
inject<IUserAuthService>().value,
|
inject<IUserAuthService>().value,
|
||||||
inject<IMetaRepository>().value,
|
inject<IMetaService>().value,
|
||||||
inject<IJwtRefreshTokenRepository>().value,
|
|
||||||
inject<IUserRepository>().value,
|
inject<IUserRepository>().value,
|
||||||
inject<IdGenerateService>().value
|
inject<IJwtService>().value
|
||||||
)
|
)
|
||||||
configureRouting(
|
configureRouting(
|
||||||
inject<HttpSignatureVerifyService>().value,
|
inject<HttpSignatureVerifyService>().value,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.exception
|
||||||
|
|
||||||
|
class InvalidRefreshTokenException : IllegalArgumentException{
|
||||||
|
constructor() : super()
|
||||||
|
constructor(s: String?) : super(s)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.exception
|
||||||
|
|
||||||
|
class NotInitException : Exception {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
|
||||||
|
message,
|
||||||
|
cause,
|
||||||
|
enableSuppression,
|
||||||
|
writableStackTrace
|
||||||
|
)
|
||||||
|
}
|
|
@ -3,23 +3,16 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import com.auth0.jwk.JwkProviderBuilder
|
import com.auth0.jwk.JwkProviderBuilder
|
||||||
import com.auth0.jwt.JWT
|
|
||||||
import com.auth0.jwt.algorithms.Algorithm
|
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
||||||
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
|
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.property
|
import dev.usbharu.hideout.property
|
||||||
import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository
|
|
||||||
import dev.usbharu.hideout.repository.IMetaRepository
|
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
import dev.usbharu.hideout.service.IJwtService
|
||||||
|
import dev.usbharu.hideout.service.IMetaService
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import dev.usbharu.hideout.service.IdGenerateService
|
|
||||||
import dev.usbharu.hideout.util.Base64Util
|
|
||||||
import dev.usbharu.hideout.util.JsonWebKeyUtil
|
import dev.usbharu.hideout.util.JsonWebKeyUtil
|
||||||
import dev.usbharu.hideout.util.RsaUtil
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
|
@ -27,38 +20,23 @@ import io.ktor.server.auth.jwt.*
|
||||||
import io.ktor.server.request.*
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.time.Instant
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
const val TOKEN_AUTH = "jwt-auth"
|
const val TOKEN_AUTH = "jwt-auth"
|
||||||
|
|
||||||
fun Application.configureSecurity(
|
fun Application.configureSecurity(
|
||||||
userAuthService: IUserAuthService,
|
userAuthService: IUserAuthService,
|
||||||
metaRepository: IMetaRepository,
|
metaService: IMetaService,
|
||||||
refreshTokenRepository: IJwtRefreshTokenRepository,
|
|
||||||
userRepository: IUserRepository,
|
userRepository: IUserRepository,
|
||||||
idGenerateService: IdGenerateService
|
jwtService: IJwtService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val privateKey = runBlocking {
|
|
||||||
RsaUtil.decodeRsaPrivateKey(Base64Util.decode(requireNotNull(metaRepository.get()).jwt.privateKey))
|
|
||||||
}
|
|
||||||
val publicKey = runBlocking {
|
|
||||||
val publicKey = requireNotNull(metaRepository.get()).jwt.publicKey
|
|
||||||
RsaUtil.decodeRsaPublicKey(Base64Util.decode(publicKey))
|
|
||||||
}
|
|
||||||
val issuer = property("hideout.url")
|
val issuer = property("hideout.url")
|
||||||
// val audience = property("jwt.audience")
|
|
||||||
val myRealm = property("jwt.realm")
|
|
||||||
val jwkProvider = JwkProviderBuilder(issuer)
|
val jwkProvider = JwkProviderBuilder(issuer)
|
||||||
.cached(10, 24, TimeUnit.HOURS)
|
.cached(10, 24, TimeUnit.HOURS)
|
||||||
.rateLimited(10, 1, TimeUnit.MINUTES)
|
.rateLimited(10, 1, TimeUnit.MINUTES)
|
||||||
.build()
|
.build()
|
||||||
install(Authentication) {
|
install(Authentication) {
|
||||||
jwt(TOKEN_AUTH) {
|
jwt(TOKEN_AUTH) {
|
||||||
realm = myRealm
|
|
||||||
verifier(jwkProvider, issuer) {
|
verifier(jwkProvider, issuer) {
|
||||||
acceptLeeway(3)
|
acceptLeeway(3)
|
||||||
|
|
||||||
|
@ -70,78 +48,34 @@ fun Application.configureSecurity(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
challenge { defaultScheme, realm ->
|
|
||||||
call.respondRedirect("/login")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
post("/login") {
|
post("/login") {
|
||||||
val user = call.receive<UserLogin>()
|
val loginUser = call.receive<UserLogin>()
|
||||||
val check = userAuthService.verifyAccount(user.username, user.password)
|
val check = userAuthService.verifyAccount(loginUser.username, loginUser.password)
|
||||||
if (check.not()) {
|
if (check.not()) {
|
||||||
return@post call.respond(HttpStatusCode.Unauthorized)
|
return@post call.respond(HttpStatusCode.Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
val findByNameAndDomain = userRepository.findByNameAndDomain(user.username, Config.configData.domain)
|
val user = userRepository.findByNameAndDomain(loginUser.username, Config.configData.domain)
|
||||||
?: throw UserNotFoundException("${user.username} was not found.")
|
?: throw UserNotFoundException("${loginUser.username} was not found.")
|
||||||
|
|
||||||
val token = JWT.create()
|
return@post call.respond(jwtService.createToken(user))
|
||||||
.withAudience("${Config.configData.url}/users/${user.username}")
|
|
||||||
.withIssuer(issuer)
|
|
||||||
.withKeyId(metaRepository.get()?.jwt?.kid.toString())
|
|
||||||
.withClaim("username", user.username)
|
|
||||||
.withExpiresAt(Date(System.currentTimeMillis() + 60000))
|
|
||||||
.sign(Algorithm.RSA256(publicKey, privateKey))
|
|
||||||
val refreshToken = UUID.randomUUID().toString()
|
|
||||||
refreshTokenRepository.save(
|
|
||||||
JwtRefreshToken(
|
|
||||||
idGenerateService.generateId(), findByNameAndDomain.id, refreshToken, Instant.now(),
|
|
||||||
Instant.ofEpochMilli(Instant.now().toEpochMilli() + 1209600033)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return@post call.respond(JwtToken(token, refreshToken))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
post("/refresh-token") {
|
post("/refresh-token") {
|
||||||
val refreshToken = call.receive<RefreshToken>()
|
val refreshToken = call.receive<RefreshToken>()
|
||||||
val findByToken = refreshTokenRepository.findByToken(refreshToken.refreshToken)
|
return@post call.respond(jwtService.refreshToken(refreshToken))
|
||||||
?: return@post call.respondText("token not found",status = HttpStatusCode.Forbidden)
|
|
||||||
|
|
||||||
if (findByToken.createdAt.isAfter(Instant.now())) {
|
|
||||||
return@post call.respondText("created_at", status = HttpStatusCode.Forbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findByToken.expiresAt.isBefore(Instant.now())) {
|
|
||||||
return@post call.respondText( "expires_at", status = HttpStatusCode.Forbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
val user = userRepository.findById(findByToken.userId)
|
|
||||||
?: throw UserNotFoundException("${findByToken.userId} was not found.")
|
|
||||||
val token = JWT.create()
|
|
||||||
.withAudience("${Config.configData.url}/users/${user.name}")
|
|
||||||
.withIssuer(issuer)
|
|
||||||
.withKeyId(metaRepository.get()?.jwt?.kid.toString())
|
|
||||||
.withClaim("username", user.name)
|
|
||||||
.withExpiresAt(Date(System.currentTimeMillis() + 60000))
|
|
||||||
.sign(Algorithm.RSA256(publicKey, privateKey))
|
|
||||||
val newRefreshToken = UUID.randomUUID().toString()
|
|
||||||
refreshTokenRepository.save(
|
|
||||||
JwtRefreshToken(
|
|
||||||
idGenerateService.generateId(), user.id, newRefreshToken, Instant.now(),
|
|
||||||
Instant.ofEpochMilli(Instant.now().toEpochMilli() + 1209600033)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return@post call.respond(JwtToken(token, newRefreshToken))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/.well-known/jwks.json") {
|
get("/.well-known/jwks.json") {
|
||||||
//language=JSON
|
//language=JSON
|
||||||
val meta = requireNotNull(metaRepository.get())
|
val jwt = metaService.getJwtMeta()
|
||||||
call.respondText(
|
call.respondText(
|
||||||
contentType = ContentType.Application.Json,
|
contentType = ContentType.Application.Json,
|
||||||
text = JsonWebKeyUtil.publicKeyToJwk(meta.jwt.publicKey, meta.jwt.kid.toString())
|
text = JsonWebKeyUtil.publicKeyToJwk(jwt.publicKey, jwt.kid.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,18 @@ package dev.usbharu.hideout.repository
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
||||||
|
|
||||||
interface IJwtRefreshTokenRepository {
|
interface IJwtRefreshTokenRepository {
|
||||||
|
suspend fun generateId():Long
|
||||||
|
|
||||||
suspend fun save(token: JwtRefreshToken)
|
suspend fun save(token: JwtRefreshToken)
|
||||||
|
|
||||||
suspend fun findById(id:Long):JwtRefreshToken?
|
suspend fun findById(id:Long):JwtRefreshToken?
|
||||||
suspend fun findByToken(token:String):JwtRefreshToken?
|
suspend fun findByToken(token:String):JwtRefreshToken?
|
||||||
|
suspend fun findByUserId(userId:Long):JwtRefreshToken?
|
||||||
|
|
||||||
|
suspend fun delete(token:JwtRefreshToken)
|
||||||
|
suspend fun deleteById(id:Long)
|
||||||
|
suspend fun deleteByToken(token:String)
|
||||||
|
suspend fun deleteByUserId(userId:Long)
|
||||||
|
|
||||||
|
suspend fun deleteAll()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
package dev.usbharu.hideout.repository
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
||||||
|
import dev.usbharu.hideout.service.IdGenerateService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
class JwtRefreshTokenRepositoryImpl(private val database: Database) : IJwtRefreshTokenRepository {
|
class JwtRefreshTokenRepositoryImpl(
|
||||||
|
private val database: Database,
|
||||||
|
private val idGenerateService: IdGenerateService
|
||||||
|
) :
|
||||||
|
IJwtRefreshTokenRepository {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
|
@ -19,6 +25,8 @@ class JwtRefreshTokenRepositoryImpl(private val database: Database) : IJwtRefres
|
||||||
suspend fun <T> query(block: suspend () -> T): T =
|
suspend fun <T> query(block: suspend () -> T): T =
|
||||||
newSuspendedTransaction(Dispatchers.IO) { block() }
|
newSuspendedTransaction(Dispatchers.IO) { block() }
|
||||||
|
|
||||||
|
override suspend fun generateId(): Long = idGenerateService.generateId()
|
||||||
|
|
||||||
override suspend fun save(token: JwtRefreshToken) {
|
override suspend fun save(token: JwtRefreshToken) {
|
||||||
query {
|
query {
|
||||||
if (JwtRefreshTokens.select { JwtRefreshTokens.id.eq(token.id) }.empty()) {
|
if (JwtRefreshTokens.select { JwtRefreshTokens.id.eq(token.id) }.empty()) {
|
||||||
|
@ -51,6 +59,42 @@ class JwtRefreshTokenRepositoryImpl(private val database: Database) : IJwtRefres
|
||||||
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.singleOrNull()?.toJwtRefreshToken()
|
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.singleOrNull()?.toJwtRefreshToken()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findByUserId(userId: Long): JwtRefreshToken? {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }.singleOrNull()?.toJwtRefreshToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(token: JwtRefreshToken) {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq token.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteById(id: Long) {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteByToken(token: String) {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.refreshToken eq token }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteByUserId(userId: Long) {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.userId eq userId }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteAll() {
|
||||||
|
return query {
|
||||||
|
JwtRefreshTokens.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ResultRow.toJwtRefreshToken(): JwtRefreshToken {
|
fun ResultRow.toJwtRefreshToken(): JwtRefreshToken {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
||||||
|
|
||||||
|
interface IJwtService {
|
||||||
|
suspend fun createToken(user:User):JwtToken
|
||||||
|
suspend fun refreshToken(refreshToken: RefreshToken):JwtToken
|
||||||
|
|
||||||
|
suspend fun revokeToken(refreshToken: RefreshToken)
|
||||||
|
suspend fun revokeToken(user:User)
|
||||||
|
suspend fun revokeAll()
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.Meta
|
||||||
|
|
||||||
|
interface IMetaService {
|
||||||
|
suspend fun getMeta(): Meta
|
||||||
|
suspend fun updateMeta(meta: Meta)
|
||||||
|
suspend fun getJwtMeta(): Jwt
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
||||||
|
import dev.usbharu.hideout.exception.InvalidRefreshTokenException
|
||||||
|
import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository
|
||||||
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
|
import dev.usbharu.hideout.util.RsaUtil
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class JwtServiceImpl(
|
||||||
|
private val metaService: IMetaService,
|
||||||
|
private val refreshTokenRepository: IJwtRefreshTokenRepository,
|
||||||
|
private val userService: IUserService
|
||||||
|
) : IJwtService {
|
||||||
|
|
||||||
|
private val privateKey by lazy {
|
||||||
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
|
RsaUtil.decodeRsaPrivateKey(metaService.getJwtMeta().privateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val publicKey by lazy {
|
||||||
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
|
RsaUtil.decodeRsaPublicKey(metaService.getJwtMeta().publicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val keyId by lazy {
|
||||||
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
|
metaService.getJwtMeta().kid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun createToken(user: User): JwtToken {
|
||||||
|
val now = Instant.now()
|
||||||
|
val token = JWT.create()
|
||||||
|
.withAudience("${Config.configData.url}/users/${user.id}")
|
||||||
|
.withIssuer(Config.configData.url)
|
||||||
|
.withKeyId(keyId.await().toString())
|
||||||
|
.withClaim("username", user.name)
|
||||||
|
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||||
|
.sign(Algorithm.RSA256(publicKey.await(), privateKey.await()))
|
||||||
|
|
||||||
|
val jwtRefreshToken = JwtRefreshToken(
|
||||||
|
id = refreshTokenRepository.generateId(),
|
||||||
|
userId = user.id,
|
||||||
|
refreshToken = UUID.randomUUID().toString(),
|
||||||
|
createdAt = now,
|
||||||
|
expiresAt = now.plus(14, ChronoUnit.DAYS)
|
||||||
|
)
|
||||||
|
refreshTokenRepository.save(jwtRefreshToken)
|
||||||
|
return JwtToken(token, jwtRefreshToken.refreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun refreshToken(refreshToken: RefreshToken): JwtToken {
|
||||||
|
val token = refreshTokenRepository.findByToken(refreshToken.refreshToken)
|
||||||
|
?: throw InvalidRefreshTokenException("Invalid Refresh Token")
|
||||||
|
|
||||||
|
val user = userService.findById(token.userId)
|
||||||
|
|
||||||
|
val now = Instant.now()
|
||||||
|
if (token.createdAt.isAfter(now)) {
|
||||||
|
throw InvalidRefreshTokenException("Invalid Refresh Token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.expiresAt.isBefore(now)) {
|
||||||
|
throw InvalidRefreshTokenException("Refresh Token Expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
return createToken(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun revokeToken(refreshToken: RefreshToken) {
|
||||||
|
refreshTokenRepository.deleteByToken(refreshToken.refreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun revokeToken(user: User) {
|
||||||
|
refreshTokenRepository.deleteByUserId(user.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun revokeAll() {
|
||||||
|
refreshTokenRepository.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.Meta
|
||||||
|
import dev.usbharu.hideout.exception.NotInitException
|
||||||
|
import dev.usbharu.hideout.repository.IMetaRepository
|
||||||
|
|
||||||
|
class MetaServiceImpl(private val metaRepository: IMetaRepository) : IMetaService {
|
||||||
|
override suspend fun getMeta(): Meta = metaRepository.get() ?: throw NotInitException("Meta is null")
|
||||||
|
|
||||||
|
override suspend fun updateMeta(meta: Meta) {
|
||||||
|
metaRepository.save(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getJwtMeta(): Jwt = getMeta().jwt
|
||||||
|
}
|
|
@ -12,8 +12,12 @@ object RsaUtil {
|
||||||
return KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
return KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun decodeRsaPublicKey(encoded: String): RSAPublicKey = decodeRsaPublicKey(Base64Util.decode(encoded))
|
||||||
|
|
||||||
fun decodeRsaPrivateKey(byteArray: ByteArray):RSAPrivateKey{
|
fun decodeRsaPrivateKey(byteArray: ByteArray):RSAPrivateKey{
|
||||||
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(byteArray)
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(byteArray)
|
||||||
return KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
return KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun decodeRsaPrivateKey(encoded: String):RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue