From 65919cd3798bc574bc51f33e1a2c5962558b097d Mon Sep 17 00:00:00 2001 From: seokjin8678 <seokjin8678@gmail.com> Date: Tue, 7 May 2024 19:48:11 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20OpenIdUserInfoProvider=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20OpenIdClient=EC=97=90=20=EB=B9=84?= =?UTF-8?q?=EC=A6=88=EB=8B=88=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 불필요한 depth 제거 --- .../auth/domain/OpenIdUserInfoProvider.java | 6 --- .../openid/KakaoOpenIdClient.java | 54 +++++++++++++++++-- ...erTest.java => KakaoOpenIdClientTest.java} | 27 +++++----- 3 files changed, 63 insertions(+), 24 deletions(-) delete mode 100644 backend/src/main/java/com/festago/auth/domain/OpenIdUserInfoProvider.java rename backend/src/test/java/com/festago/auth/infrastructure/{KakaoOpenIdUserInfoProviderTest.java => KakaoOpenIdClientTest.java} (86%) diff --git a/backend/src/main/java/com/festago/auth/domain/OpenIdUserInfoProvider.java b/backend/src/main/java/com/festago/auth/domain/OpenIdUserInfoProvider.java deleted file mode 100644 index f721d40d4..000000000 --- a/backend/src/main/java/com/festago/auth/domain/OpenIdUserInfoProvider.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.festago.auth.domain; - -public interface OpenIdUserInfoProvider { - - UserInfo provide(String idToken); -} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java index 5c2fbd0be..09e1c4cd1 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java @@ -1,20 +1,66 @@ package com.festago.auth.infrastructure.openid; import com.festago.auth.domain.OpenIdClient; +import com.festago.auth.domain.OpenIdNonceValidator; import com.festago.auth.domain.SocialType; import com.festago.auth.domain.UserInfo; -import lombok.RequiredArgsConstructor; +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.UnauthorizedException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import java.time.Clock; +import java.util.Date; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +@Slf4j @Component -@RequiredArgsConstructor public class KakaoOpenIdClient implements OpenIdClient { - private final KakaoOpenIdUserInfoProvider kakaoIdTokenUserInfoProvider; + private static final String ISSUER = "https://kauth.kakao.com"; + private final OpenIdNonceValidator openIdNonceValidator; + private final OpenIdIdTokenParser idTokenParser; + private final Set<String> appKeys; + + public KakaoOpenIdClient( + @Value("${festago.oauth2.kakao.rest-api-key}") String restApiKey, + @Value("${festago.oauth2.kakao.native-app-key}") String nativeAppKey, + KakaoOpenIdPublicKeyLocator kakaoOpenIdPublicKeyLocator, + OpenIdNonceValidator openIdNonceValidator, + Clock clock + ) { + this.appKeys = Set.of(restApiKey, nativeAppKey); + this.openIdNonceValidator = openIdNonceValidator; + this.idTokenParser = new OpenIdIdTokenParser(Jwts.parser() + .keyLocator(kakaoOpenIdPublicKeyLocator) + .requireIssuer(ISSUER) + .clock(() -> Date.from(clock.instant())) + .build()); + } @Override public UserInfo getUserInfo(String idToken) { - return kakaoIdTokenUserInfoProvider.provide(idToken); + Claims payload = idTokenParser.parse(idToken); + openIdNonceValidator.validate(payload.get("nonce", String.class), payload.getExpiration()); + validateAudience(payload.getAudience()); + return UserInfo.builder() + .socialType(SocialType.KAKAO) + .socialId(payload.getSubject()) + .nickname(payload.get("nickname", String.class)) + .profileImage(payload.get("picture", String.class)) + .build(); + } + + private void validateAudience(Set<String> audiences) { + for (String audience : audiences) { + if (appKeys.contains(audience)) { + return; + } + } + log.info("허용되지 않는 id 토큰의 audience 값이 요청되었습니다. audiences={}", audiences); + throw new UnauthorizedException(ErrorCode.OPEN_ID_INVALID_TOKEN); } @Override diff --git a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdClientTest.java similarity index 86% rename from backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java rename to backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdClientTest.java index 0f03442c9..314d5bcf0 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdClientTest.java @@ -1,11 +1,10 @@ package com.festago.auth.infrastructure; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.mock; -import static org.mockito.BDDMockito.spy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import com.festago.auth.infrastructure.openid.KakaoOpenIdPublicKeyLocator; import com.festago.auth.infrastructure.openid.KakaoOpenIdUserInfoProvider; @@ -28,9 +27,9 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class KakaoOpenIdUserInfoProviderTest { +class KakaoOpenIdClientTest { - KakaoOpenIdUserInfoProvider kakaoOpenIdUserInfoProvider; + KakaoOpenIdClient kakaoOpenIdClient; KakaoOpenIdPublicKeyLocator keyLocator; @@ -42,7 +41,7 @@ class KakaoOpenIdUserInfoProviderTest { void setUp() { keyLocator = mock(); clock = spy(Clock.systemDefaultZone()); - kakaoOpenIdUserInfoProvider = new KakaoOpenIdUserInfoProvider( + kakaoOpenIdClient = new KakaoOpenIdClient( "restApiKey", "nativeAppKey", keyLocator, @@ -65,7 +64,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> kakaoOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> kakaoOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -84,7 +83,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> kakaoOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> kakaoOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -104,7 +103,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> kakaoOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> kakaoOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -124,7 +123,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> kakaoOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> kakaoOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -143,7 +142,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> kakaoOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> kakaoOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -164,7 +163,7 @@ void setUp() { .compact(); // when - var expect = kakaoOpenIdUserInfoProvider.provide(idToken); + var expect = kakaoOpenIdClient.getUserInfo(idToken); // then assertThat(expect.socialId()).isEqualTo(socialId); @@ -187,7 +186,7 @@ void setUp() { .compact(); // when - var expect = kakaoOpenIdUserInfoProvider.provide(idToken); + var expect = kakaoOpenIdClient.getUserInfo(idToken); // then assertThat(expect.socialId()).isEqualTo(socialId);