From 9ed5faae40f7a755ab573cac70cb3d951782cc8e Mon Sep 17 00:00:00 2001 From: Darren4641 Date: Sat, 10 Jan 2026 16:31:07 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20oauth=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=95=EC=B1=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/api/controller/AuthController.kt | 41 ++++------------- .../api/converter/AuthCommandConverter.kt | 5 --- .../auth/api/converter/AuthResultConverter.kt | 7 +-- .../com/yapp2app/auth/api/dto/AuthResponse.kt | 6 +-- .../auth/application/result/GetAuthResult.kt | 5 +-- .../usecase/KakaoRegisterUseCase.kt | 22 ++++++++- .../auth/application/usecase/LoginUseCase.kt | 45 ------------------- .../usecase/RefreshTokenUseCase.kt | 6 +-- .../filter/JwtAuthenticationFilter.kt | 16 +++---- 9 files changed, 43 insertions(+), 110 deletions(-) delete mode 100644 src/main/kotlin/com/yapp2app/auth/application/usecase/LoginUseCase.kt diff --git a/src/main/kotlin/com/yapp2app/auth/api/controller/AuthController.kt b/src/main/kotlin/com/yapp2app/auth/api/controller/AuthController.kt index 59624d4..90d9ac5 100644 --- a/src/main/kotlin/com/yapp2app/auth/api/controller/AuthController.kt +++ b/src/main/kotlin/com/yapp2app/auth/api/controller/AuthController.kt @@ -5,11 +5,8 @@ import com.yapp2app.auth.api.converter.AuthResultConverter import com.yapp2app.auth.api.dto.CreateAuthRequest import com.yapp2app.auth.api.dto.GetAuthResponse import com.yapp2app.auth.api.dto.GetKakaoTokenResponse -import com.yapp2app.auth.api.dto.GetTokenResponse -import com.yapp2app.auth.api.dto.LoginRequest import com.yapp2app.auth.api.dto.RefreshTokenRequest import com.yapp2app.auth.application.usecase.KakaoRegisterUseCase -import com.yapp2app.auth.application.usecase.LoginUseCase import com.yapp2app.auth.application.usecase.RefreshTokenUseCase import com.yapp2app.common.api.dto.BaseResponse import io.swagger.v3.oas.annotations.Hidden @@ -36,7 +33,6 @@ import org.springframework.web.bind.annotation.RestController @RestController class AuthController( private val kakaoRegisterUseCase: KakaoRegisterUseCase, - private val loginUseCase: LoginUseCase, private val refreshTokenUseCase: RefreshTokenUseCase, private val commandConverter: AuthCommandConverter, private val resultConverter: AuthResultConverter, @@ -62,26 +58,6 @@ class AuthController( [staging] https://kauth.kakao.com/oauth/authorize?client_id=4db94315d17162e99b36029f6f9775c6&redirect_uri=https://dev-yapp.suitestudy.com:4641/api/auth/test/kakao/redirect&response_type=code&scope=openid,profile_nickname,profile_image 응답의 `id_token` 필드 값을 이 API의 `idToken`으로 사용하세요. - """, - ) - @ApiResponses( - ApiResponse(responseCode = "200", description = "카카오 OIDC 엔드포인트가 정상적으로 작동합니다."), - ) - @PostMapping("/kakao/register") - fun kakaoRegister(@RequestBody @Valid request: CreateAuthRequest): BaseResponse { - val command = commandConverter.toCreateAuthCommand(request) - - val result = kakaoRegisterUseCase.execute(command) - - val response = resultConverter.toCreateAuthResponse(result) - - return BaseResponse(data = response) - } - - @Operation( - summary = "로그인", - description = """ - ## 로그인 API 사용자의 OID와 ProviderType을 사용하여 로그인을 수행합니다. @@ -107,16 +83,15 @@ class AuthController( """, ) @ApiResponses( - ApiResponse(responseCode = "200", description = "로그인 성공"), - ApiResponse(responseCode = "400", description = "인증 실패 - 가입되지 않은 사용자"), + ApiResponse(responseCode = "200", description = "카카오 OIDC 엔드포인트가 정상적으로 작동합니다."), ) - @PostMapping("/login") - fun login(@RequestBody @Valid request: LoginRequest): BaseResponse { - val command = commandConverter.toLoginAuthCommand(request) + @PostMapping("/kakao/login") + fun kakaoRegister(@RequestBody @Valid request: CreateAuthRequest): BaseResponse { + val command = commandConverter.toCreateAuthCommand(request) - val result = loginUseCase.execute(command) + val result = kakaoRegisterUseCase.execute(command) - val response = resultConverter.toLoginAuthResponse(result) + val response = resultConverter.toCreateAuthResponse(result) return BaseResponse(data = response) } @@ -150,12 +125,12 @@ class AuthController( ), ) @PostMapping("/refresh") - fun refreshToken(@RequestBody @Valid request: RefreshTokenRequest): BaseResponse { + fun refreshToken(@RequestBody @Valid request: RefreshTokenRequest): BaseResponse { val command = commandConverter.toRefreshTokenCommand(request) val result = refreshTokenUseCase.execute(command) - val response = resultConverter.toLoginAuthResponse(result) + val response = resultConverter.toCreateAuthResponse(result) return BaseResponse(data = response) } diff --git a/src/main/kotlin/com/yapp2app/auth/api/converter/AuthCommandConverter.kt b/src/main/kotlin/com/yapp2app/auth/api/converter/AuthCommandConverter.kt index 5a0f1f3..25fac63 100644 --- a/src/main/kotlin/com/yapp2app/auth/api/converter/AuthCommandConverter.kt +++ b/src/main/kotlin/com/yapp2app/auth/api/converter/AuthCommandConverter.kt @@ -1,9 +1,7 @@ package com.yapp2app.auth.api.converter import com.yapp2app.auth.api.dto.CreateAuthRequest -import com.yapp2app.auth.api.dto.LoginRequest import com.yapp2app.auth.api.dto.RefreshTokenRequest -import com.yapp2app.auth.application.command.LoginCommand import com.yapp2app.auth.application.command.RefreshTokenCommand import com.yapp2app.auth.application.command.RegisterKakaoUserCommand import org.springframework.stereotype.Component @@ -14,9 +12,6 @@ class AuthCommandConverter { fun toCreateAuthCommand(request: CreateAuthRequest): RegisterKakaoUserCommand = RegisterKakaoUserCommand(request.idToken) - fun toLoginAuthCommand(request: LoginRequest): LoginCommand = - LoginCommand(oid = request.oid, providerType = request.providerType) - fun toRefreshTokenCommand(request: RefreshTokenRequest): RefreshTokenCommand = RefreshTokenCommand(request.refreshToken) } diff --git a/src/main/kotlin/com/yapp2app/auth/api/converter/AuthResultConverter.kt b/src/main/kotlin/com/yapp2app/auth/api/converter/AuthResultConverter.kt index 5113bfe..dd84e06 100644 --- a/src/main/kotlin/com/yapp2app/auth/api/converter/AuthResultConverter.kt +++ b/src/main/kotlin/com/yapp2app/auth/api/converter/AuthResultConverter.kt @@ -1,17 +1,12 @@ package com.yapp2app.auth.api.converter import com.yapp2app.auth.api.dto.GetAuthResponse -import com.yapp2app.auth.api.dto.GetTokenResponse import com.yapp2app.auth.application.result.GetAuthResult -import com.yapp2app.auth.application.result.GetTokenResult import org.springframework.stereotype.Component @Component class AuthResultConverter { fun toCreateAuthResponse(result: GetAuthResult): GetAuthResponse = - GetAuthResponse(oid = result.oid, providerType = result.providerType) - - fun toLoginAuthResponse(result: GetTokenResult): GetTokenResponse = - GetTokenResponse(accessToken = result.accessToken, result.refreshToken) + GetAuthResponse(accessToken = result.accessToken, result.refreshToken) } diff --git a/src/main/kotlin/com/yapp2app/auth/api/dto/AuthResponse.kt b/src/main/kotlin/com/yapp2app/auth/api/dto/AuthResponse.kt index 08fcc6d..b7bbdc7 100644 --- a/src/main/kotlin/com/yapp2app/auth/api/dto/AuthResponse.kt +++ b/src/main/kotlin/com/yapp2app/auth/api/dto/AuthResponse.kt @@ -1,16 +1,12 @@ package com.yapp2app.auth.api.dto -import com.yapp2app.user.domain.enums.ProviderType - /** * fileName : AuthResponse * author : darren * date : 2025. 12. 26. 18:05 * description : Auth aggregate에 대한 응답 */ -data class GetAuthResponse(val oid: Long, val providerType: ProviderType) - -data class GetTokenResponse(val accessToken: String, val refreshToken: String) +data class GetAuthResponse(val accessToken: String, val refreshToken: String) /** * REST_API TEST용 DTO diff --git a/src/main/kotlin/com/yapp2app/auth/application/result/GetAuthResult.kt b/src/main/kotlin/com/yapp2app/auth/application/result/GetAuthResult.kt index ef34253..ccb43d3 100644 --- a/src/main/kotlin/com/yapp2app/auth/application/result/GetAuthResult.kt +++ b/src/main/kotlin/com/yapp2app/auth/application/result/GetAuthResult.kt @@ -1,13 +1,10 @@ package com.yapp2app.auth.application.result -import com.yapp2app.user.domain.enums.ProviderType - /** * fileName : AuthResult * author : darren * date : 2025. 12. 29. 14:23 * description : */ -data class GetAuthResult(val oid: Long, val providerType: ProviderType) -data class GetTokenResult(val accessToken: String, val refreshToken: String) +data class GetAuthResult(val accessToken: String, val refreshToken: String) diff --git a/src/main/kotlin/com/yapp2app/auth/application/usecase/KakaoRegisterUseCase.kt b/src/main/kotlin/com/yapp2app/auth/application/usecase/KakaoRegisterUseCase.kt index dda1b12..d6c795b 100644 --- a/src/main/kotlin/com/yapp2app/auth/application/usecase/KakaoRegisterUseCase.kt +++ b/src/main/kotlin/com/yapp2app/auth/application/usecase/KakaoRegisterUseCase.kt @@ -8,6 +8,7 @@ import com.yapp2app.auth.application.port.OauthHelperPort import com.yapp2app.auth.application.port.OidcPort import com.yapp2app.auth.application.result.GetAuthResult import com.yapp2app.auth.infra.security.properties.OauthProperties +import com.yapp2app.auth.infra.security.token.AuthTokenProvider import com.yapp2app.common.annotation.UseCase import com.yapp2app.common.transaction.TransactionRunner import com.yapp2app.user.application.port.UserRepositoryPort @@ -30,6 +31,7 @@ class KakaoRegisterUseCase( @Qualifier("kakaoOidcAdapter") private val oidcPort: OidcPort, @Qualifier("kakaoOauthHelper") private val oauthHelperPort: OauthHelperPort, private val restClient: RestClient, + private val tokenProvider: AuthTokenProvider, private val userRepositoryPort: UserRepositoryPort, private val transactionRunner: TransactionRunner, ) { @@ -51,7 +53,25 @@ class KakaoRegisterUseCase( val user = transactionRunner.run { registerKakaoUserIfEmpty(oauthInfoResponse) } - return GetAuthResult(oid = user.oid, providerType = user.providerType) + // JWT 토큰 생성 + val accessToken = tokenProvider.createAccessToken( + id = user.id.toString(), + roles = user.roles.split(","), + name = user.name, + providerType = user.providerType, + ) + + val refreshToken = tokenProvider.createRefreshToken( + id = user.id.toString(), + roles = user.roles.split(","), + name = user.name, + providerType = user.providerType, + ) + + return GetAuthResult( + accessToken = accessToken, + refreshToken = refreshToken, + ) } private fun registerKakaoUserIfEmpty(oauthInfoResponse: OauthInfoResponse): User { diff --git a/src/main/kotlin/com/yapp2app/auth/application/usecase/LoginUseCase.kt b/src/main/kotlin/com/yapp2app/auth/application/usecase/LoginUseCase.kt deleted file mode 100644 index 06ce2f6..0000000 --- a/src/main/kotlin/com/yapp2app/auth/application/usecase/LoginUseCase.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.yapp2app.auth.application.usecase - -import com.yapp2app.auth.application.command.LoginCommand -import com.yapp2app.auth.application.result.GetTokenResult -import com.yapp2app.auth.infra.security.token.AuthTokenProvider -import com.yapp2app.common.annotation.UseCase -import com.yapp2app.common.api.dto.ResultCode -import com.yapp2app.common.exception.BusinessException -import com.yapp2app.user.application.port.UserRepositoryPort - -/** - * fileName : LoginUseCase - * author : darren - * date : 2025. 12. 30. 18:05 - * description : 스프링 시큐리티를 사용한 로그인 UseCase - */ -@UseCase -class LoginUseCase(private val tokenProvider: AuthTokenProvider, private val userRepositoryPort: UserRepositoryPort) { - - fun execute(loginCommand: LoginCommand): GetTokenResult { - val user = userRepositoryPort.findByOid(loginCommand.oid, loginCommand.providerType) ?: throw BusinessException( - ResultCode.NOT_FOUND_USER, - ) - - // JWT 토큰 생성 - val accessToken = tokenProvider.createAccessToken( - id = user.id.toString(), - roles = user.roles.split(","), - name = user.name, - providerType = user.providerType, - ) - - val refreshToken = tokenProvider.createRefreshToken( - id = user.id.toString(), - roles = user.roles.split(","), - name = user.name, - providerType = user.providerType, - ) - - return GetTokenResult( - accessToken = accessToken, - refreshToken = refreshToken, - ) - } -} diff --git a/src/main/kotlin/com/yapp2app/auth/application/usecase/RefreshTokenUseCase.kt b/src/main/kotlin/com/yapp2app/auth/application/usecase/RefreshTokenUseCase.kt index 502b228..ccc3549 100644 --- a/src/main/kotlin/com/yapp2app/auth/application/usecase/RefreshTokenUseCase.kt +++ b/src/main/kotlin/com/yapp2app/auth/application/usecase/RefreshTokenUseCase.kt @@ -1,7 +1,7 @@ package com.yapp2app.auth.application.usecase import com.yapp2app.auth.application.command.RefreshTokenCommand -import com.yapp2app.auth.application.result.GetTokenResult +import com.yapp2app.auth.application.result.GetAuthResult import com.yapp2app.auth.infra.security.token.AuthTokenProvider import com.yapp2app.auth.infra.security.token.UserPrincipal import com.yapp2app.common.annotation.UseCase @@ -17,7 +17,7 @@ import com.yapp2app.common.exception.BusinessException @UseCase class RefreshTokenUseCase(private val tokenProvider: AuthTokenProvider) { - fun execute(command: RefreshTokenCommand): GetTokenResult { + fun execute(command: RefreshTokenCommand): GetAuthResult { // 1. RefreshToken 유효성 검증 if (!tokenProvider.validateRefreshToken(command.refreshToken)) { throw BusinessException(ResultCode.INVALID_TOKEN_ERROR) @@ -43,7 +43,7 @@ class RefreshTokenUseCase(private val tokenProvider: AuthTokenProvider) { providerType = userPrincipal.providerType, ) - return GetTokenResult( + return GetAuthResult( accessToken = newAccessToken, refreshToken = newRefreshToken, ) diff --git a/src/main/kotlin/com/yapp2app/auth/infra/security/filter/JwtAuthenticationFilter.kt b/src/main/kotlin/com/yapp2app/auth/infra/security/filter/JwtAuthenticationFilter.kt index 5ee5f1f..6616a0c 100644 --- a/src/main/kotlin/com/yapp2app/auth/infra/security/filter/JwtAuthenticationFilter.kt +++ b/src/main/kotlin/com/yapp2app/auth/infra/security/filter/JwtAuthenticationFilter.kt @@ -39,17 +39,17 @@ class JwtAuthenticationFilter(private val tokenProvider: AuthTokenProvider) : On } filterChain.doFilter(request, response) } catch (ex: SignatureException) { - handleException(response, ResultCode.INVALID_TOKEN_ERROR) + handleException(response, ResultCode.INVALID_TOKEN_ERROR, HttpServletResponse.SC_FORBIDDEN) } catch (ex: SecurityException) { - handleException(response, ResultCode.INVALID_TOKEN_ERROR) + handleException(response, ResultCode.INVALID_TOKEN_ERROR, HttpServletResponse.SC_FORBIDDEN) } catch (ex: MalformedJwtException) { - handleException(response, ResultCode.INVALID_TOKEN_ERROR) + handleException(response, ResultCode.INVALID_TOKEN_ERROR, HttpServletResponse.SC_FORBIDDEN) } catch (ex: ExpiredJwtException) { - handleException(response, ResultCode.EXPIRED_TOKEN_ERROR) + handleException(response, ResultCode.EXPIRED_TOKEN_ERROR, HttpServletResponse.SC_UNAUTHORIZED) } catch (ex: UnsupportedJwtException) { - handleException(response, ResultCode.EXPIRED_TOKEN_ERROR) + handleException(response, ResultCode.EXPIRED_TOKEN_ERROR, HttpServletResponse.SC_UNAUTHORIZED) } catch (ex: Exception) { - handleException(response, ResultCode.SECURITY_ERROR) + handleException(response, ResultCode.SECURITY_ERROR, HttpServletResponse.SC_FORBIDDEN) } } @@ -88,12 +88,12 @@ class JwtAuthenticationFilter(private val tokenProvider: AuthTokenProvider) : On * - **대응**: 재로그인이 필요합니다. `/api/auth/login` API를 호출하세요. * - **재로그인 필요 여부**: 예 */ - private fun handleException(response: HttpServletResponse, resultCode: ResultCode) { + private fun handleException(response: HttpServletResponse, resultCode: ResultCode, status: Int) { val jsonObject = JsonObject() response.contentType = "application/json;charset=UTF-8" response.characterEncoding = "utf-8" - response.status = HttpServletResponse.SC_UNAUTHORIZED + response.status = status jsonObject.addProperty("resultCode", resultCode.code) jsonObject.addProperty("message", resultCode.message)