Skip to content

Commit

Permalink
[YS-114] fix: OAuth 로직 버그 수정 및 Auth에 대한 리팩터링 적용 (#22)
Browse files Browse the repository at this point in the history
* refact: move naver response dto to naver package

* feat: add NaverAuthGateway for DIP

* refact: rename OAuthService to AuthService

* refact: add generate token's logic to AuthService

* refact: move naver response to naver package

* refact: refactor naver login logic for clean architecture

* refact: refactor AuthController/Service for clean architecture

* refact: add findByOauthEmailAndStatus repository's method to MemberGateway

* refact: rename memberId to id in Member

* refact: change email's api response for unification

* refact: change not null to nullable in OauthLoginResponse

* feat: add UseCase to UseCase class

* refact: delete unused file

* feat: add GoogleAuthGateway for clean-architecture

* refact: move NaverAuthGateway to feign package

* test: add FetchGoogleUserInfoUseCase test

* refact: refactor FetchGoogleUserInfoUseCase logic

* refact: refactor signInWithGoogle Controller due to changed logic

* style: rename variables for clarity

* refact: refactor FetchNaverUserInfoUseCase logic

* feat: add EnableConfigurationProperties for OAuth

* fix: fix GoogleAuthFeignClient logic

* feat: add ContentLengthRequestInterceptor due to feign-client's error

* refact: refactor google auth and naver auth logic

* refact: delete unused annotation

* refact: change return value

* style: add blank

* refactor: move class file or code

* refactor: delete unused file

* feat: add Service annotation

* test: delete unused name field in response

* refact: delete unused annotation

* refact: delete unused import

* refact: move feign interceptor to feign package

* refact: delete unused code
  • Loading branch information
Ji-soo708 authored Jan 9, 2025
1 parent 03b03d5 commit acf71b2
Show file tree
Hide file tree
Showing 46 changed files with 569 additions and 516 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies {
implementation("org.mariadb.jdbc:mariadb-java-client:2.7.3")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
implementation ("io.awspring.cloud:spring-cloud-starter-aws:2.4.4")
implementation("io.awspring.cloud:spring-cloud-starter-aws:2.4.4")
implementation("com.github.in-seo:univcert:master-SNAPSHOT") {
exclude(group = "org.hamcrest", module= "harmcest-core")
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.dobby.backend.application.service

import com.dobby.backend.application.usecase.*
import org.springframework.stereotype.Service

@Service
class AuthService(
private val fetchGoogleUserInfoUseCase: FetchGoogleUserInfoUseCase,
private val fetchNaverUserInfoUseCase: FetchNaverUserInfoUseCase,
private val generateTokenWithRefreshTokenUseCase: GenerateTokenWithRefreshTokenUseCase,
private val generateTestTokenUseCase: GenerateTestTokenUseCase,
) {
fun getGoogleUserInfo(authorizationCode: String): FetchGoogleUserInfoUseCase.Output {
val result = fetchGoogleUserInfoUseCase.execute(
FetchGoogleUserInfoUseCase.Input(
authorizationCode = authorizationCode
)
)
return FetchGoogleUserInfoUseCase.Output(
isRegistered = result.isRegistered,
accessToken = result.accessToken,
refreshToken = result.refreshToken,
memberId = result.memberId,
name = result.name,
oauthEmail = result.oauthEmail,
role = result.role,
provider = result.provider,
)
}

fun getNaverUserInfo(authorizationCode: String, state: String): FetchNaverUserInfoUseCase.Output {
val result = fetchNaverUserInfoUseCase.execute(
FetchNaverUserInfoUseCase.Input(
authorizationCode = authorizationCode,
state = state
)
)
return FetchNaverUserInfoUseCase.Output(
isRegistered = result.isRegistered,
accessToken = result.accessToken,
refreshToken = result.refreshToken,
memberId = result.memberId,
name = result.name,
oauthEmail = result.oauthEmail,
role = result.role,
provider = result.provider,
)
}

fun forceToken(memberId: Long): GenerateTestTokenUseCase.Output {
val result = generateTestTokenUseCase.execute(
GenerateTestTokenUseCase.Input(
memberId = memberId
)
)
return GenerateTestTokenUseCase.Output(
accessToken = result.accessToken,
refreshToken = result.refreshToken,
member = result.member
)
}

fun signInWithRefreshToken(refreshToken: String): GenerateTokenWithRefreshTokenUseCase.Output {
val result = generateTokenWithRefreshTokenUseCase.execute(
GenerateTokenWithRefreshTokenUseCase.Input(
refreshToken = refreshToken,
)
)
return GenerateTokenWithRefreshTokenUseCase.Output(
accessToken = result.accessToken,
refreshToken = result.refreshToken,
member = result.member
)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,64 +1,65 @@
package com.dobby.backend.application.usecase

import com.dobby.backend.application.mapper.OauthUserMapper
import com.dobby.backend.domain.exception.SignInMemberException
import com.dobby.backend.infrastructure.config.properties.GoogleAuthProperties
import com.dobby.backend.domain.gateway.MemberGateway
import com.dobby.backend.domain.gateway.TokenGateway
import com.dobby.backend.domain.gateway.feign.GoogleAuthGateway
import com.dobby.backend.infrastructure.database.entity.enum.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enum.ProviderType
import com.dobby.backend.infrastructure.database.repository.MemberRepository
import com.dobby.backend.infrastructure.feign.google.GoogleAuthFeignClient
import com.dobby.backend.infrastructure.feign.google.GoogleUserInfoFeginClient
import com.dobby.backend.infrastructure.token.JwtTokenProvider
import com.dobby.backend.presentation.api.dto.request.auth.google.GoogleTokenRequest
import com.dobby.backend.presentation.api.dto.request.auth.google.GoogleOauthLoginRequest
import com.dobby.backend.presentation.api.dto.response.auth.google.GoogleTokenResponse
import com.dobby.backend.presentation.api.dto.response.auth.OauthLoginResponse
import com.dobby.backend.util.AuthenticationUtils
import com.dobby.backend.infrastructure.database.entity.enum.RoleType

class FetchGoogleUserInfoUseCase(
private val googleAuthFeignClient: GoogleAuthFeignClient,
private val googleUserInfoFeginClient: GoogleUserInfoFeginClient,
private val jwtTokenProvider: JwtTokenProvider,
private val googleAuthProperties: GoogleAuthProperties,
private val memberRepository: MemberRepository
) : UseCase<GoogleOauthLoginRequest, OauthLoginResponse> {
private val googleAuthGateway: GoogleAuthGateway,
private val memberGateway: MemberGateway,
private val jwtTokenGateway: TokenGateway
) : UseCase<FetchGoogleUserInfoUseCase.Input, FetchGoogleUserInfoUseCase.Output> {

override fun execute(input: GoogleOauthLoginRequest): OauthLoginResponse {
try {
val googleTokenRequest = GoogleTokenRequest(
code = input.authorizationCode,
clientId = googleAuthProperties.clientId,
clientSecret = googleAuthProperties.clientSecret,
redirectUri = googleAuthProperties.redirectUri
)
data class Input(
val authorizationCode: String
)

val oauthRes = fetchAccessToken(googleTokenRequest)
val oauthToken = oauthRes.accessToken
data class Output(
val isRegistered: Boolean,
val accessToken: String?,
val refreshToken: String?,
val memberId: Long?,
val name: String?,
val oauthEmail: String,
val role: RoleType?,
val provider: ProviderType
)

val userInfo = googleUserInfoFeginClient.getUserInfo("Bearer $oauthToken")
val email = userInfo.email
val regMember = memberRepository.findByOauthEmailAndStatus(email, MemberStatus.ACTIVE)
?: throw SignInMemberException()
override fun execute(input: Input): Output {
val oauthToken = googleAuthGateway.getAccessToken(input.authorizationCode).accessToken
val userInfo = googleAuthGateway.getUserInfo(oauthToken)
val email = userInfo.email
val member = email.let { memberGateway.findByOauthEmailAndStatus(it, MemberStatus.ACTIVE) }

val regMemberAuthentication = AuthenticationUtils.createAuthentication(regMember)
val jwtAccessToken = jwtTokenProvider.generateAccessToken(regMemberAuthentication)
val jwtRefreshToken = jwtTokenProvider.generateRefreshToken(regMemberAuthentication)
return if (member != null) {
val jwtAccessToken = jwtTokenGateway.generateAccessToken(member)
val jwtRefreshToken = jwtTokenGateway.generateRefreshToken(member)

return OauthUserMapper.toDto(
Output(
isRegistered = true,
accessToken = jwtAccessToken,
refreshToken = jwtRefreshToken,
oauthEmail = regMember.oauthEmail,
oauthName = regMember.name ?: throw SignInMemberException(),
role = regMember.role ?: throw SignInMemberException(),
memberId = member.id,
name = member.name,
oauthEmail = member.oauthEmail,
role = member.role,
provider = ProviderType.GOOGLE
)
} else {
// 등록된 멤버가 없으면 isRegistered = false, memberId = null
Output(
isRegistered = false,
accessToken = null,
refreshToken = null,
memberId = null,
name = null,
oauthEmail = email,
role = null,
provider = ProviderType.GOOGLE
)
} catch (e: SignInMemberException) {
throw SignInMemberException()
}
}

private fun fetchAccessToken(googleTokenRequest: GoogleTokenRequest): GoogleTokenResponse {
return googleAuthFeignClient.getAccessToken(googleTokenRequest)
}
}
Original file line number Diff line number Diff line change
@@ -1,65 +1,67 @@
package com.dobby.backend.application.usecase

import com.dobby.backend.application.mapper.OauthUserMapper
import com.dobby.backend.domain.exception.SignInMemberException
import com.dobby.backend.infrastructure.config.properties.NaverAuthProperties
import com.dobby.backend.domain.gateway.MemberGateway
import com.dobby.backend.domain.gateway.feign.NaverAuthGateway
import com.dobby.backend.domain.gateway.TokenGateway
import com.dobby.backend.infrastructure.database.entity.enum.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enum.ProviderType
import com.dobby.backend.infrastructure.database.repository.MemberRepository
import com.dobby.backend.infrastructure.feign.naver.NaverAuthFeignClient
import com.dobby.backend.infrastructure.feign.naver.NaverUserInfoFeignClient
import com.dobby.backend.infrastructure.token.JwtTokenProvider
import com.dobby.backend.presentation.api.dto.request.auth.NaverOauthLoginRequest
import com.dobby.backend.presentation.api.dto.request.auth.NaverTokenRequest
import com.dobby.backend.presentation.api.dto.response.auth.NaverTokenResponse
import com.dobby.backend.presentation.api.dto.response.auth.OauthLoginResponse
import com.dobby.backend.util.AuthenticationUtils
import com.dobby.backend.infrastructure.database.entity.enum.RoleType

class FetchNaverUserInfoUseCase(
private val naverAuthFeignClient: NaverAuthFeignClient,
private val naverUserInfoFeginClient: NaverUserInfoFeignClient,
private val jwtTokenProvider: JwtTokenProvider,
private val naverAuthProperties: NaverAuthProperties,
private val memberRepository: MemberRepository
) : UseCase<NaverOauthLoginRequest, OauthLoginResponse> {
private val naverAuthGateway: NaverAuthGateway,
private val memberGateway: MemberGateway,
private val jwtTokenGateway: TokenGateway
) : UseCase<FetchNaverUserInfoUseCase.Input, FetchNaverUserInfoUseCase.Output> {

override fun execute(input: NaverOauthLoginRequest): OauthLoginResponse {
try {
val naverTokenRequest = NaverTokenRequest(
grantType = "authorization_code",
clientId = naverAuthProperties.clientId,
clientSecret = naverAuthProperties.clientSecret,
code = input.authorizationCode,
state = input.state
)
data class Input(
val authorizationCode: String,
val state: String
)

val oauthRes = fetchAccessToken(naverTokenRequest)
val oauthToken = oauthRes.accessToken
// TODO: 테스트 후, oauthEmail not null 처리
data class Output(
val isRegistered: Boolean,
val accessToken: String?,
val refreshToken: String?,
val memberId: Long?,
val name: String?,
val oauthEmail: String?,
val role: RoleType?,
val provider: ProviderType
)

val userInfo = naverUserInfoFeginClient.getUserInfo("Bearer $oauthToken")
val email = userInfo.email
val regMember = memberRepository.findByOauthEmailAndStatus(email, MemberStatus.ACTIVE)
?: throw SignInMemberException()
override fun execute(input: Input): Output {
val oauthToken = naverAuthGateway.getAccessToken(input.authorizationCode, input.state).accessToken
val userInfo = oauthToken?.let { naverAuthGateway.getUserInfo(it) }
val email = userInfo?.email
val member = email?.let { memberGateway.findByOauthEmailAndStatus(it, MemberStatus.ACTIVE) }

val regMemberAuthentication = AuthenticationUtils.createAuthentication(regMember)
val jwtAccessToken = jwtTokenProvider.generateAccessToken(regMemberAuthentication)
val jwtRefreshToken = jwtTokenProvider.generateRefreshToken(regMemberAuthentication)
return if (member != null) {
val jwtAccessToken = jwtTokenGateway.generateAccessToken(member)
val jwtRefreshToken = jwtTokenGateway.generateRefreshToken(member)

return OauthUserMapper.toDto(
Output(
isRegistered = true,
accessToken = jwtAccessToken,
refreshToken = jwtRefreshToken,
oauthEmail = regMember.oauthEmail,
oauthName = regMember.name ?: throw SignInMemberException(),
role = regMember.role ?: throw SignInMemberException(),
memberId = member.id,
name = member.name,
oauthEmail = member.oauthEmail,
role = member.role,
provider = ProviderType.NAVER
)
} else {
// 등록된 멤버가 없으면 isRegistered = false, memberId = null
Output(
isRegistered = false,
accessToken = null,
refreshToken = null,
memberId = null,
name = null,
oauthEmail = email,
role = null,
provider = ProviderType.NAVER
)
} catch (e: SignInMemberException) {
throw SignInMemberException()
}
}

private fun fetchAccessToken(naverTokenRequest: NaverTokenRequest): NaverTokenResponse {
return naverAuthFeignClient.getAccessToken(naverTokenRequest)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,29 @@ package com.dobby.backend.application.usecase

import com.dobby.backend.domain.gateway.MemberGateway
import com.dobby.backend.domain.gateway.TokenGateway
import com.dobby.backend.domain.model.member.Member

class GenerateTestToken(
class GenerateTestTokenUseCase(
private val tokenGateway: TokenGateway,
private val memberGateway: MemberGateway,
) : UseCase<GenerateTestToken.Input, GenerateTestToken.Output> {
) : UseCase<GenerateTestTokenUseCase.Input, GenerateTestTokenUseCase.Output> {
data class Input(
val memberId: Long
)

data class Output(
val accessToken: String,
val refreshToken: String,
val member: Member
)

override fun execute(input: Input): Output {
val memberId = input.memberId
val member = memberGateway.getById(memberId)
return Output(
accessToken = tokenGateway.generateAccessToken(member),
refreshToken = tokenGateway.generateRefreshToken(member)
refreshToken = tokenGateway.generateRefreshToken(member),
member = member
)
}
}
Loading

0 comments on commit acf71b2

Please sign in to comment.