Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

๐Ÿ”€ :: ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ API ๊ตฌํ˜„ #40

Merged
merged 9 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import andreas311.miso.domain.auth.adapter.input.mapper.AuthDataMapper
import andreas311.miso.domain.auth.application.port.input.LogoutUseCase
import andreas311.miso.domain.auth.application.port.input.SignInUseCase
import andreas311.miso.domain.auth.application.port.input.SignUpUseCase
import andreas311.miso.domain.auth.application.port.input.TokenReissueUseCase
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.*
import javax.validation.Valid

@RequestController("/auth")
class AuthAdapter(
private val authDataMapper: AuthDataMapper,
private val signUpUseCase: SignUpUseCase,
private val signInUseCase: SignInUseCase,
private val logoutUseCase: LogoutUseCase
private val logoutUseCase: LogoutUseCase,
private val tokenReissueUseCase: TokenReissueUseCase
) {
@PostMapping
fun signUp(@RequestBody @Valid signUpRequest: SignUpRequest): ResponseEntity<Void> =
Expand All @@ -37,4 +37,10 @@ class AuthAdapter(
fun logout(): ResponseEntity<Void> =
logoutUseCase.execute()
.let { ResponseEntity.status(HttpStatus.NO_CONTENT).build() }

@PatchMapping
fun reissue(@RequestHeader("Refresh-Token") refreshToken: String): ResponseEntity<TokenResponse> =
tokenReissueUseCase.execute(refreshToken)
.let { authDataMapper toResponse it }
.let { ResponseEntity.ok(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import andreas311.miso.domain.auth.adapter.output.persistence.mapper.RefreshToke
import andreas311.miso.domain.auth.adapter.output.persistence.repository.RefreshTokenRepository
import andreas311.miso.domain.auth.application.port.output.QueryRefreshTokenPort
import andreas311.miso.domain.auth.domain.RefreshToken
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
import java.util.*

Expand All @@ -16,4 +17,9 @@ class QueryRefreshTokenPersistenceAdapter(
val refreshTokenEntity = refreshTokenRepository.findByUserId(userId)
return refreshTokenMapper.toDomain(refreshTokenEntity)
}

override fun findByRefreshTokenOrNull(refreshToken: String): RefreshToken? {
val refreshTokenEntity = refreshTokenRepository.findByIdOrNull(refreshToken)
return refreshTokenMapper.toDomain(refreshTokenEntity)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package andreas311.miso.domain.auth.application.port.input

import andreas311.miso.domain.auth.application.port.output.dto.TokenDto

interface TokenReissueUseCase {
fun execute(refreshToken: String): TokenDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import java.util.UUID

interface QueryRefreshTokenPort {
fun findByUserIdOrNull(userId: UUID): RefreshToken?

fun findByRefreshTokenOrNull(refreshToken: String): RefreshToken?
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import javax.servlet.http.HttpServletRequest
interface TokenParsePort {
fun parseAccessToken(request: HttpServletRequest): String?

fun parseRefreshTokenToken(refreshToken: String): String?
fun parseRefreshToken(refreshToken: String): String?

fun authentication(token: String): Authentication
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package andreas311.miso.domain.auth.application.service

import andreas311.miso.common.annotation.RollbackService
import andreas311.miso.domain.auth.application.event.SaveRefreshTokenEvent
import andreas311.miso.domain.auth.application.port.input.TokenReissueUseCase
import andreas311.miso.domain.auth.application.port.output.QueryRefreshTokenPort
import andreas311.miso.domain.auth.application.port.output.TokenGeneratePort
import andreas311.miso.domain.auth.application.port.output.TokenParsePort
import andreas311.miso.domain.auth.application.port.output.dto.TokenDto
import andreas311.miso.domain.user.application.exception.UserNotFoundException
import andreas311.miso.domain.user.application.port.output.QueryUserPort
import andreas311.miso.domain.user.domain.User
import andreas311.miso.global.security.jwt.common.exception.InvalidTokenTypeException
import andreas311.miso.global.security.jwt.common.exception.TokenExpiredException
import org.springframework.context.ApplicationEventPublisher

@RollbackService
class TokenReissueService(
private val queryUserPort: QueryUserPort,
private val tokenParsePort: TokenParsePort,
private val tokenGeneratePort: TokenGeneratePort,
private val queryRefreshTokenPort: QueryRefreshTokenPort,
private val applicationEventPublisher: ApplicationEventPublisher
) : TokenReissueUseCase {
override fun execute(refreshToken: String): TokenDto {
val parsedRefreshToken = tokenParsePort.parseRefreshToken(refreshToken)
?: throw InvalidTokenTypeException()

val refreshToken = queryRefreshTokenPort.findByRefreshTokenOrNull(parsedRefreshToken)
?: throw TokenExpiredException()

val user = queryUserPort.findByIdOrNull(refreshToken.userId)
?: throw UserNotFoundException()

val token = tokenGeneratePort.generateToken(user.email, user.role)

publishSaveRefreshToken(token, user)

return token
}

private fun publishSaveRefreshToken(token: TokenDto, user: User) {
val saveRefreshTokenEvent = SaveRefreshTokenEvent(
userId = user.id,
refreshToken = token.refreshToken
)
applicationEventPublisher.publishEvent(saveRefreshTokenEvent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import andreas311.miso.domain.user.adapter.output.persistence.mapper.UserMapper
import andreas311.miso.domain.user.adapter.output.persistence.repository.UserRepository
import andreas311.miso.domain.user.application.port.output.QueryUserPort
import andreas311.miso.domain.user.domain.User
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
import java.util.*

@Component
class QueryUserPersistenceAdapter(
Expand All @@ -18,4 +20,9 @@ class QueryUserPersistenceAdapter(

override fun existsByEmail(email: String): Boolean =
userRepository.existsByEmail(email)

override fun findByIdOrNull(id: UUID): User? {
val userEntity = userRepository.findByIdOrNull(id)
return userMapper.toDomain(userEntity)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import andreas311.miso.domain.user.domain.User

interface CommandUserPort {
fun saveUser(user: User): User

fun deleteByEmail(email: String)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package andreas311.miso.domain.user.application.port.output

import andreas311.miso.domain.user.domain.User
import java.util.UUID

interface QueryUserPort {
fun findByEmailOrNull(email: String): User?

fun existsByEmail(email: String): Boolean

fun findByIdOrNull(id: UUID): User?
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class TokenParseAdapter(
.let { it ?: return null }
.let { if (it.startsWith(JwtProperties.TOKEN_PREFIX)) it.replace(JwtProperties.TOKEN_PREFIX, "") else null }

override fun parseRefreshTokenToken(refreshToken: String): String? =
override fun parseRefreshToken(refreshToken: String): String? =
if (refreshToken.startsWith(JwtProperties.TOKEN_PREFIX)) refreshToken.replace(JwtProperties.TOKEN_PREFIX, "") else null

override fun authentication(token: String): Authentication =
Expand Down
Loading