Skip to content

Commit 34a89c5

Browse files
Darren4641darren
andauthored
fix/#106 -> staging merge commit
* fix: 카카오 로그인 시 platform 파람 추가 할당 * fix: 프론트 검증을 위해 임시 배포 * fix: spotless 적용 * fix: 엑세스, 리프레쉬 토큰 expiry 변경 * fix: deploy-staging.yml fix/#106 브랜치 제거 * fix: auth Platform enum으로 타입 세이프 --------- Co-authored-by: darren <darren@darrenui-MacBookPro.local>
1 parent c551b97 commit 34a89c5

File tree

14 files changed

+90
-19
lines changed

14 files changed

+90
-19
lines changed

src/main/kotlin/com/yapp2app/auth/api/converter/AuthCommandConverter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.yapp2app.auth.api.dto.CreateAuthRequest
44
import com.yapp2app.auth.api.dto.RefreshTokenRequest
55
import com.yapp2app.auth.application.command.RefreshTokenCommand
66
import com.yapp2app.auth.application.command.RegisterOauthUserCommand
7+
import com.yapp2app.auth.domain.Platform
78
import com.yapp2app.user.domain.enums.ProviderType
89
import org.springframework.stereotype.Component
910

@@ -14,6 +15,7 @@ class AuthCommandConverter {
1415
RegisterOauthUserCommand(
1516
idToken = request.idToken!!,
1617
providerType = ProviderType.from(providerTypeStr),
18+
platform = Platform.from(request.platform),
1719
)
1820

1921
fun toRefreshTokenCommand(request: RefreshTokenRequest): RefreshTokenCommand =
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package com.yapp2app.auth.api.dto
22

33
import jakarta.validation.constraints.NotBlank
4+
import jakarta.validation.constraints.Pattern
45

56
/**
67
* fileName : AuthRequest
78
* author : darren
89
* date : 2025. 12. 26. 18:05
910
* description : 인증/인가 관련 요청 body
1011
*/
11-
data class CreateAuthRequest(@field:NotBlank(message = "ID 토큰은 필수 입니다") val idToken: String?)
12+
data class CreateAuthRequest(
13+
@field:NotBlank(message = "ID 토큰은 필수 입니다")
14+
val idToken: String?,
15+
@field:Pattern(regexp = "^(android|ios)$", message = "플랫폼은 android 또는 ios만 가능합니다")
16+
val platform: String? = null,
17+
)
1218

1319
data class RefreshTokenRequest(@field:NotBlank(message = "Refresh 토큰은 필수입니다") val refreshToken: String?)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.yapp2app.auth.application.command
22

3+
import com.yapp2app.auth.domain.Platform
34
import com.yapp2app.user.domain.enums.ProviderType
45

56
/**
@@ -8,6 +9,6 @@ import com.yapp2app.user.domain.enums.ProviderType
89
* date : 2025. 12. 12. 13:18
910
* description : 인증/인가 관련 API
1011
*/
11-
data class RegisterOauthUserCommand(val idToken: String, val providerType: ProviderType)
12+
data class RegisterOauthUserCommand(val idToken: String, val providerType: ProviderType, val platform: Platform)
1213

1314
data class RefreshTokenCommand(val refreshToken: String)

src/main/kotlin/com/yapp2app/auth/application/port/OidcTokenValidatorPort.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.yapp2app.auth.application.port
22

33
import com.yapp2app.auth.application.contract.OauthInfoResponse
4+
import com.yapp2app.auth.domain.Platform
45
import com.yapp2app.user.domain.enums.ProviderType
56

67
/**
@@ -10,5 +11,5 @@ import com.yapp2app.user.domain.enums.ProviderType
1011
* description :
1112
*/
1213
interface OidcTokenValidatorPort {
13-
fun validateIdToken(idToken: String, providerType: ProviderType): OauthInfoResponse
14+
fun validateIdToken(idToken: String, providerType: ProviderType, platform: Platform): OauthInfoResponse
1415
}

src/main/kotlin/com/yapp2app/auth/application/usecase/OauthLoginUseCase.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ class OauthLoginUseCase(
4545
* 4. oauthInfoResult 값 여부에 따라 회원가입 처리
4646
*/
4747
fun execute(command: RegisterOauthUserCommand): GetAuthResult {
48-
val oauthInfoResponse = oidcTokenValidatorPort.validateIdToken(command.idToken, command.providerType)
48+
val oauthInfoResponse = oidcTokenValidatorPort.validateIdToken(
49+
command.idToken,
50+
command.providerType,
51+
command.platform,
52+
)
4953

5054
// 신규 사용자 추가
5155
val (user, _) = transactionRunner.run { registerOauthUserIfEmpty(oauthInfoResponse) }
@@ -105,7 +109,7 @@ class OauthLoginUseCase(
105109
* @throws Exception 토큰 획득 실패 시
106110
*/
107111
fun getAccessTokenByCode(code: String): GetKakaoTokenResponse {
108-
val clientId = oauthProperties.kakao.clientId
112+
val clientId = oauthProperties.kakao.androidClientId
109113
val clientSecret = oauthProperties.kakao.clientSecret
110114

111115
val params = LinkedMultiValueMap<String, String>()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.yapp2app.auth.domain
2+
3+
/**
4+
* fileName : Platform
5+
* author : darren
6+
* date : 2026. 2. 4
7+
* description :
8+
*/
9+
enum class Platform(val value: String) {
10+
ANDROID("android"),
11+
IOS("ios"),
12+
;
13+
14+
companion object {
15+
fun from(value: String?): Platform = Platform.entries.firstOrNull { it.value == value }
16+
?: ANDROID
17+
// TODO 안드로이드 심사 끝나면 해당 예외로 변경?: throw BusinessException(ResultCode.INVALID_PARAMETER)
18+
}
19+
}

src/main/kotlin/com/yapp2app/auth/infra/oauth/OidcTokenValidator.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.yapp2app.auth.infra.oauth
33
import com.yapp2app.auth.application.contract.AuthCacheKeys
44
import com.yapp2app.auth.application.contract.OauthInfoResponse
55
import com.yapp2app.auth.application.port.OidcTokenValidatorPort
6+
import com.yapp2app.auth.domain.Platform
67
import com.yapp2app.auth.infra.oauth.helper.OauthHelper
78
import com.yapp2app.auth.infra.oauth.oidc.Oidc
89
import com.yapp2app.auth.infra.oauth.registry.OauthHelperRegistry
@@ -33,18 +34,18 @@ class OidcTokenValidator(
3334
* - 1차 시도: 캐시된 공개키로 토큰 검증
3435
* - BusinessException 발생 시: 캐시 무효화 후 재시도 (공개키 로테이션 대응)
3536
*/
36-
override fun validateIdToken(idToken: String, providerType: ProviderType): OauthInfoResponse {
37+
override fun validateIdToken(idToken: String, providerType: ProviderType, platform: Platform): OauthInfoResponse {
3738
val oidcAdapter = oidcRegistry.getAdapter(providerType)
3839
val oauthHelperAdapter = oauthHelperRegistry.getAdapter(providerType)
3940

4041
return try {
4142
// 1차 시도: 캐시된 공개키로 토큰 검증
42-
validateTokenWithPublicKeys(idToken, oidcAdapter, oauthHelperAdapter)
43+
validateTokenWithPublicKeys(idToken, oidcAdapter, oauthHelperAdapter, platform)
4344
} catch (e: BusinessException) {
4445
log.info("Kakao OIDC token expired. 갱신 로직")
4546
// 캐시 무효화 후 재시도
4647
authRedisCacheAdapter.clearPublicKeys(AuthCacheKeys.KAKAO_OIDC_KEY)
47-
validateTokenWithPublicKeys(idToken, oidcAdapter, oauthHelperAdapter)
48+
validateTokenWithPublicKeys(idToken, oidcAdapter, oauthHelperAdapter, platform)
4849
} catch (e: Exception) {
4950
e.printStackTrace() // TODO 인증 실패 예외 로그 모니터링용
5051
throw e
@@ -56,11 +57,17 @@ class OidcTokenValidator(
5657
* - 공개키는 Redis에 캐싱되어 있을 수 있음
5758
* - 검증 실패 시 BusinessException 발생
5859
*/
59-
private fun validateTokenWithPublicKeys(idToken: String, oidc: Oidc, oauthHelper: OauthHelper): OauthInfoResponse {
60+
private fun validateTokenWithPublicKeys(
61+
idToken: String,
62+
oidc: Oidc,
63+
oauthHelper: OauthHelper,
64+
platform: Platform,
65+
): OauthInfoResponse {
6066
val publicKeys = oidc.getOIDCPublicKey()
6167
return oauthHelper.getOauthInfoByIdToken(
6268
idToken = idToken,
6369
publicKeys = publicKeys,
70+
platform = platform,
6471
)
6572
}
6673
}

src/main/kotlin/com/yapp2app/auth/infra/oauth/helper/AppleOauthHelper.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.yapp2app.auth.application.contract.OIDCDecodePayloadResponse
55
import com.yapp2app.auth.application.contract.OIDCPublicKeyDto
66
import com.yapp2app.auth.application.contract.OIDCPublicKeysResponse
77
import com.yapp2app.auth.application.contract.OauthInfoResponse
8+
import com.yapp2app.auth.domain.Platform
89
import com.yapp2app.auth.infra.security.properties.OauthProperties
910
import com.yapp2app.common.api.dto.ResultCode
1011
import com.yapp2app.common.exception.BusinessException
@@ -58,14 +59,19 @@ class AppleOauthHelper(private val oauthProperties: OauthProperties, private val
5859
* @param publicKeys Apple 공개키 목록
5960
* @return OAuth 정보 (Provider 타입, OID, email)
6061
*/
61-
override fun getOauthInfoByIdToken(idToken: String, publicKeys: OIDCPublicKeysResponse): OauthInfoResponse {
62+
override fun getOauthInfoByIdToken(
63+
idToken: String,
64+
publicKeys: OIDCPublicKeysResponse,
65+
platform: Platform,
66+
): OauthInfoResponse {
6267
// Step 1: 헤더에서 kid 추출
6368
val kid = extractKidFromTokenHeader(idToken)
6469

6570
// Step 2: kid로 jwks 조회
6671
val publicKey = findPublicKeyByKid(publicKeys.keys, kid)
6772

6873
// Step 3-6: 토큰 검증 및 Claims 추출
74+
// Apple은 플랫폼 구분 불필요, 동일한 clientId 사용
6975
val payload = validateTokenAndExtractPayload(
7076
token = idToken,
7177
publicKey = publicKey,

src/main/kotlin/com/yapp2app/auth/infra/oauth/helper/KakaoOauthHelper.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.yapp2app.auth.application.contract.OIDCDecodePayloadResponse
55
import com.yapp2app.auth.application.contract.OIDCPublicKeyDto
66
import com.yapp2app.auth.application.contract.OIDCPublicKeysResponse
77
import com.yapp2app.auth.application.contract.OauthInfoResponse
8+
import com.yapp2app.auth.domain.Platform
89
import com.yapp2app.auth.infra.security.properties.OauthProperties
910
import com.yapp2app.common.api.dto.ResultCode
1011
import com.yapp2app.common.exception.BusinessException
@@ -56,19 +57,29 @@ class KakaoOauthHelper(private val oauthProperties: OauthProperties, private val
5657
* @param publicKeys 카카오 공개키 목록
5758
* @return OAuth 정보 (Provider 타입, OID)
5859
*/
59-
override fun getOauthInfoByIdToken(idToken: String, publicKeys: OIDCPublicKeysResponse): OauthInfoResponse {
60+
override fun getOauthInfoByIdToken(
61+
idToken: String,
62+
publicKeys: OIDCPublicKeysResponse,
63+
platform: Platform,
64+
): OauthInfoResponse {
6065
// Step 1: 헤더에서 kid 추출 (토큰 분리 및 Base64 디코딩)
6166
val kid = extractKidFromTokenHeader(idToken)
6267

6368
// Step 2: kid로 jwks 조회
6469
val publicKey = findPublicKeyByKid(publicKeys.keys, kid)
6570

6671
// Step 3-7: 토큰 검증 및 Claims 추출 (iss, aud, exp, 서명 검증)
72+
// 플랫폼별 clientId 선택
73+
val expectedAudience = when (platform) {
74+
Platform.IOS -> oauthProperties.kakao.iosClientId
75+
Platform.ANDROID -> oauthProperties.kakao.androidClientId
76+
}
77+
6778
val payload = validateTokenAndExtractPayload(
6879
token = idToken,
6980
publicKey = publicKey,
7081
expectedIssuer = oauthProperties.kakao.issuer,
71-
expectedAudience = oauthProperties.kakao.clientId,
82+
expectedAudience = expectedAudience,
7283
)
7384

7485
return OauthInfoResponse(

src/main/kotlin/com/yapp2app/auth/infra/oauth/helper/OauthHelper.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.yapp2app.auth.infra.oauth.helper
22

33
import com.yapp2app.auth.application.contract.OIDCPublicKeysResponse
44
import com.yapp2app.auth.application.contract.OauthInfoResponse
5+
import com.yapp2app.auth.domain.Platform
56

67
/**
78
* fileName : OauthHelperPort
@@ -10,5 +11,9 @@ import com.yapp2app.auth.application.contract.OauthInfoResponse
1011
* description : OAuth OIDC 검증을 위한 Port
1112
*/
1213
interface OauthHelper {
13-
fun getOauthInfoByIdToken(idToken: String, publicKeys: OIDCPublicKeysResponse): OauthInfoResponse
14+
fun getOauthInfoByIdToken(
15+
idToken: String,
16+
publicKeys: OIDCPublicKeysResponse,
17+
platform: Platform,
18+
): OauthInfoResponse
1419
}

0 commit comments

Comments
 (0)