diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java index 02ff8b49b..df228713c 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java @@ -1,20 +1,63 @@ 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 AppleOpenIdClient implements OpenIdClient { - private final AppleOpenIdUserInfoProvider appleOpenIdUserInfoProvider; + private static final String ISSUER = "https://appleid.apple.com"; + private final OpenIdNonceValidator openIdNonceValidator; + private final OpenIdIdTokenParser idTokenParser; + private final String clientId; + + public AppleOpenIdClient( + @Value("${festago.oauth2.apple.client-id}") String appleClientId, + AppleOpenIdPublicKeyLocator appleOpenIdPublicKeyLocator, + OpenIdNonceValidator openIdNonceValidator, + Clock clock + ) { + this.clientId = appleClientId; + this.openIdNonceValidator = openIdNonceValidator; + this.idTokenParser = new OpenIdIdTokenParser(Jwts.parser() + .keyLocator(appleOpenIdPublicKeyLocator) + .requireIssuer(ISSUER) + .clock(() -> Date.from(clock.instant())) + .build()); + } @Override public UserInfo getUserInfo(String idToken) { - return appleOpenIdUserInfoProvider.provide(idToken); + Claims payload = idTokenParser.parse(idToken); + openIdNonceValidator.validate(payload.get("nonce", String.class), payload.getExpiration()); + validateAudience(payload.getAudience()); + return UserInfo.builder() + .socialType(SocialType.APPLE) + .socialId(payload.getSubject()) + .build(); + } + + private void validateAudience(Set audiences) { + for (String audience : audiences) { + if (clientId.equals(audience)) { + return; + } + } + log.info("허용되지 않는 id 토큰의 audience 값이 요청되었습니다. audiences={}", audiences); + throw new UnauthorizedException(ErrorCode.OPEN_ID_INVALID_TOKEN); } @Override diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProvider.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProvider.java deleted file mode 100644 index 07677d131..000000000 --- a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProvider.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.festago.auth.infrastructure.openid; - -import com.festago.auth.domain.OpenIdNonceValidator; -import com.festago.auth.domain.OpenIdUserInfoProvider; -import com.festago.auth.domain.SocialType; -import com.festago.auth.domain.UserInfo; -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 -public class AppleOpenIdUserInfoProvider implements OpenIdUserInfoProvider { - - private static final String ISSUER = "https://appleid.apple.com"; - private final OpenIdNonceValidator openIdNonceValidator; - private final OpenIdIdTokenParser idTokenParser; - private final String clientId; - - public AppleOpenIdUserInfoProvider( - @Value("${festago.oauth2.apple.client-id}") String appleClientId, - AppleOpenIdPublicKeyLocator appleOpenIdPublicKeyLocator, - OpenIdNonceValidator openIdNonceValidator, - Clock clock - ) { - this.clientId = appleClientId; - this.openIdNonceValidator = openIdNonceValidator; - this.idTokenParser = new OpenIdIdTokenParser(Jwts.parser() - .keyLocator(appleOpenIdPublicKeyLocator) - .requireIssuer(ISSUER) - .clock(() -> Date.from(clock.instant())) - .build()); - } - - @Override - public UserInfo provide(String idToken) { - Claims payload = idTokenParser.parse(idToken); - openIdNonceValidator.validate(payload.get("nonce", String.class), payload.getExpiration()); - validateAudience(payload.getAudience()); - return UserInfo.builder() - .socialType(SocialType.APPLE) - .socialId(payload.getSubject()) - .build(); - } - - private void validateAudience(Set audiences) { - for (String audience : audiences) { - if (clientId.equals(audience)) { - return; - } - } - log.info("허용되지 않는 id 토큰의 audience 값이 요청되었습니다. audiences={}", audiences); - throw new UnauthorizedException(ErrorCode.OPEN_ID_INVALID_TOKEN); - } -} diff --git a/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProviderTest.java b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java similarity index 88% rename from backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProviderTest.java rename to backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java index b61cf5d18..bd28c0965 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdUserInfoProviderTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java @@ -23,9 +23,9 @@ @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class AppleOpenIdUserInfoProviderTest { +class AppleOpenIdClientTest { - AppleOpenIdUserInfoProvider appleOpenIdUserInfoProvider; + AppleOpenIdClient appleOpenIdClient; AppleOpenIdPublicKeyLocator keyLocator; @@ -37,7 +37,7 @@ class AppleOpenIdUserInfoProviderTest { void setUp() { keyLocator = mock(); clock = spy(Clock.systemDefaultZone()); - appleOpenIdUserInfoProvider = new AppleOpenIdUserInfoProvider( + appleOpenIdClient = new AppleOpenIdClient( "appleClientId", keyLocator, new NoopOpenIdNonceValidator(), @@ -59,7 +59,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> appleOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -78,7 +78,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> appleOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -98,7 +98,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> appleOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -118,7 +118,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> appleOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -137,7 +137,7 @@ void setUp() { .compact(); // when & then - assertThatThrownBy(() -> appleOpenIdUserInfoProvider.provide(idToken)) + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) .isInstanceOf(UnauthorizedException.class) .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); } @@ -158,7 +158,7 @@ void setUp() { .compact(); // when - var expect = appleOpenIdUserInfoProvider.provide(idToken); + var expect = appleOpenIdClient.getUserInfo(idToken); // then assertThat(expect.socialId()).isEqualTo(socialId); @@ -180,7 +180,7 @@ void setUp() { .compact(); // when - var expect = appleOpenIdUserInfoProvider.provide(idToken); + var expect = appleOpenIdClient.getUserInfo(idToken); // then assertThat(expect.socialId()).isEqualTo(socialId);