-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[YS-114] fix: OAuth 로직 버그 수정 및 Auth에 대한 리팩터링 적용 (#22)
* 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
Showing
46 changed files
with
569 additions
and
516 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 0 additions & 32 deletions
32
src/main/kotlin/com/dobby/backend/application/mapper/OauthUserMapper.kt
This file was deleted.
Oops, something went wrong.
75 changes: 75 additions & 0 deletions
75
src/main/kotlin/com/dobby/backend/application/service/AuthService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) | ||
} | ||
} |
22 changes: 0 additions & 22 deletions
22
src/main/kotlin/com/dobby/backend/application/service/OauthService.kt
This file was deleted.
Oops, something went wrong.
91 changes: 46 additions & 45 deletions
91
src/main/kotlin/com/dobby/backend/application/usecase/FetchGoogleUserInfoUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
94 changes: 48 additions & 46 deletions
94
src/main/kotlin/com/dobby/backend/application/usecase/FetchNaverUserInfoUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.