From 7b3c3eb7e8623bac9aa1185f55db16ac20c6ce70 Mon Sep 17 00:00:00 2001 From: Guga Date: Sun, 12 May 2024 22:19:30 +0900 Subject: [PATCH] =?UTF-8?q?[BE]=20feat:=20AppleOpenIdClient=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4.(#931)=20(#962)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: AppleOpenId Client 구현 * chore: 패키지 구조 변경 * test: AppleOpenIdUserInfoProvider 테스트 추가 * test: AppleOpenIdJwksClient 테스트 추가 * chore: test properties apple.clinet-id 값 추가 * feat: 만약 nickname 이 없다면 기본 닉네임을 가진다 * fix: uploadUri 의 location 뒤에 "/" 추가 * refactor: Provider 삭제 및 로직 이동 * chore: javadocs 설명 변경 * chore: 서브모듈 업데이트 --------- Co-authored-by: seokjin8678 --- .../{ => oauth2}/FestagoOAuth2Client.java | 2 +- .../KakaoOAuth2AccessTokenClient.java | 2 +- .../KakaoOAuth2AccessTokenErrorHandler.java | 2 +- .../{ => oauth2}/KakaoOAuth2Client.java | 2 +- .../KakaoOAuth2UserInfoClient.java | 2 +- .../KakaoOAuth2UserInfoErrorHandler.java | 2 +- .../openid/AppleOpenIdClient.java | 67 ++++++ .../openid/AppleOpenIdJwksClient.java | 44 ++++ .../openid/AppleOpenIdJwksErrorHandler.java | 24 +++ .../openid/AppleOpenIdPublicKeyLocator.java | 26 +++ .../openid/CachedAppleOpenIdKeyProvider.java | 63 ++++++ .../{ => openid}/CachedOpenIdKeyProvider.java | 2 +- .../{ => openid}/FestagoOpenIdClient.java | 2 +- .../{ => openid}/KakaoOpenIdClient.java | 2 +- .../{ => openid}/KakaoOpenIdJwksClient.java | 2 +- .../KakaoOpenIdJwksErrorHandler.java | 2 +- .../KakaoOpenIdPublicKeyLocator.java | 2 +- .../KakaoOpenIdUserInfoProvider.java | 2 +- .../NoopOpenIdNonceValidator.java | 2 +- .../{ => openid}/OpenIdIdTokenParser.java | 2 +- .../com/festago/member/domain/Member.java | 4 +- .../com/festago/upload/domain/UploadFile.java | 2 +- backend/src/main/resources/config | 2 +- .../auth/domain/OAuth2ClientsTest.java | 8 +- .../CachedOpenIdKeyProviderTest.java | 1 + .../KakaoOAuth2AccessTokenClientTest.java | 3 +- .../KakaoOAuth2UserInfoClientTest.java | 1 + .../KakaoOpenIdJwksClientTest.java | 1 + .../KakaoOpenIdUserInfoProviderTest.java | 3 + .../openid/AppleOpenIdClientTest.java | 190 ++++++++++++++++++ .../openid/AppleOpenIdJwksClientTest.java | 111 ++++++++++ .../com/festago/member/domain/MemberTest.java | 10 +- .../src/test/resources/application-test.yml | 2 + 33 files changed, 564 insertions(+), 28 deletions(-) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/FestagoOAuth2Client.java (96%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/KakaoOAuth2AccessTokenClient.java (97%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/KakaoOAuth2AccessTokenErrorHandler.java (98%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/KakaoOAuth2Client.java (94%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/KakaoOAuth2UserInfoClient.java (96%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => oauth2}/KakaoOAuth2UserInfoErrorHandler.java (96%) create mode 100644 backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java create mode 100644 backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClient.java create mode 100644 backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksErrorHandler.java create mode 100644 backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdPublicKeyLocator.java create mode 100644 backend/src/main/java/com/festago/auth/infrastructure/openid/CachedAppleOpenIdKeyProvider.java rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/CachedOpenIdKeyProvider.java (98%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/FestagoOpenIdClient.java (96%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/KakaoOpenIdClient.java (92%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/KakaoOpenIdJwksClient.java (97%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/KakaoOpenIdJwksErrorHandler.java (95%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/KakaoOpenIdPublicKeyLocator.java (94%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/KakaoOpenIdUserInfoProvider.java (98%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/NoopOpenIdNonceValidator.java (91%) rename backend/src/main/java/com/festago/auth/infrastructure/{ => openid}/OpenIdIdTokenParser.java (95%) create mode 100644 backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java create mode 100644 backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClientTest.java diff --git a/backend/src/main/java/com/festago/auth/infrastructure/FestagoOAuth2Client.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/FestagoOAuth2Client.java similarity index 96% rename from backend/src/main/java/com/festago/auth/infrastructure/FestagoOAuth2Client.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/FestagoOAuth2Client.java index 42042ba05..1470b330e 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/FestagoOAuth2Client.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/FestagoOAuth2Client.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.festago.auth.application.OAuth2Client; import com.festago.auth.domain.SocialType; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClient.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenClient.java similarity index 97% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClient.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenClient.java index d9bbcd53e..66c074b54 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenClient.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.festago.auth.dto.KakaoAccessTokenResponse; import org.springframework.beans.factory.annotation.Value; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenErrorHandler.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenErrorHandler.java similarity index 98% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenErrorHandler.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenErrorHandler.java index d9ee962fd..36144db02 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenErrorHandler.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2AccessTokenErrorHandler.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2Client.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2Client.java similarity index 94% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2Client.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2Client.java index a47d5df8e..36f64c87a 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2Client.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2Client.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.festago.auth.application.OAuth2Client; import com.festago.auth.domain.SocialType; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClient.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoClient.java similarity index 96% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClient.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoClient.java index 027f5ba6e..a725e5d92 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoClient.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.festago.auth.domain.UserInfo; import com.festago.auth.dto.KakaoUserInfo; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoErrorHandler.java b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoErrorHandler.java similarity index 96% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoErrorHandler.java rename to backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoErrorHandler.java index f553f8bb5..74e139bf6 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoErrorHandler.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/oauth2/KakaoOAuth2UserInfoErrorHandler.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.oauth2; import com.festago.common.exception.BadRequestException; import com.festago.common.exception.ErrorCode; 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 new file mode 100644 index 000000000..df228713c --- /dev/null +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java @@ -0,0 +1,67 @@ +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 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 AppleOpenIdClient implements OpenIdClient { + + 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) { + 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 + public SocialType getSocialType() { + return SocialType.APPLE; + } +} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClient.java new file mode 100644 index 000000000..3dc281437 --- /dev/null +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClient.java @@ -0,0 +1,44 @@ +package com.festago.auth.infrastructure.openid; + +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.InternalServerException; +import io.jsonwebtoken.io.Parser; +import io.jsonwebtoken.security.JwkSet; +import io.jsonwebtoken.security.Jwks; +import java.time.Duration; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +@Component +@Slf4j +public class AppleOpenIdJwksClient { + + private final RestTemplate restTemplate; + private final Parser parser; + + public AppleOpenIdJwksClient( + RestTemplateBuilder restTemplateBuilder + ) { + this.restTemplate = restTemplateBuilder + .errorHandler(new AppleOpenIdJwksErrorHandler()) + .setConnectTimeout(Duration.ofSeconds(2)) + .setReadTimeout(Duration.ofSeconds(3)) + .build(); + this.parser = Jwks.setParser() + .build(); + } + + public JwkSet requestGetJwks() { + try { + String jsonKeys = restTemplate.getForObject("https://appleid.apple.com/auth/keys", String.class); + log.info("Apple JWKS 공개키 목록을 조회했습니다."); + return parser.parse(jsonKeys); + } catch (ResourceAccessException e) { + log.warn("Apple JWKS 서버가 응답하지 않습니다."); + throw new InternalServerException(ErrorCode.OPEN_ID_PROVIDER_NOT_RESPONSE); + } + } +} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksErrorHandler.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksErrorHandler.java new file mode 100644 index 000000000..c0e2df2ba --- /dev/null +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksErrorHandler.java @@ -0,0 +1,24 @@ +package com.festago.auth.infrastructure.openid; + +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.InternalServerException; +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.DefaultResponseErrorHandler; + +@Slf4j +public class AppleOpenIdJwksErrorHandler extends DefaultResponseErrorHandler { + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + HttpStatusCode statusCode = response.getStatusCode(); + if (statusCode.isError()) { + log.warn("Apple JWKS 서버에서 {} 상태코드가 반환되었습니다.", statusCode.value()); + throw new InternalServerException(ErrorCode.OPEN_ID_PROVIDER_NOT_RESPONSE); + } + log.error("Apple JWKS 서버에서 알 수 없는 에러가 발생했습니다."); + throw new InternalServerException(ErrorCode.INTERNAL_SERVER_ERROR); + } +} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdPublicKeyLocator.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdPublicKeyLocator.java new file mode 100644 index 000000000..a0a20c09e --- /dev/null +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdPublicKeyLocator.java @@ -0,0 +1,26 @@ +package com.festago.auth.infrastructure.openid; + +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.UnauthorizedException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Locator; +import java.security.Key; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class AppleOpenIdPublicKeyLocator implements Locator { + + private final AppleOpenIdJwksClient appleOpenIdJwksClient; + private final CachedAppleOpenIdKeyProvider cachedOpenIdKeyProvider; + + @Override + public Key locate(Header header) { + String kid = (String) header.get("kid"); + if (kid == null) { + throw new UnauthorizedException(ErrorCode.OPEN_ID_INVALID_TOKEN); + } + return cachedOpenIdKeyProvider.provide(kid, appleOpenIdJwksClient::requestGetJwks); + } +} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/openid/CachedAppleOpenIdKeyProvider.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/CachedAppleOpenIdKeyProvider.java new file mode 100644 index 000000000..c336dbc0c --- /dev/null +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/CachedAppleOpenIdKeyProvider.java @@ -0,0 +1,63 @@ +package com.festago.auth.infrastructure.openid; + +import com.festago.common.exception.UnexpectedException; +import io.jsonwebtoken.security.JwkSet; +import jakarta.annotation.Nullable; +import java.security.Key; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class CachedAppleOpenIdKeyProvider { + + private final Map cache = new HashMap<>(); + private final ReentrantLock lock = new ReentrantLock(); + + /** + * OpenId Key를 캐싱하여 반환하는 클래스
OpenID Id Token 헤더의 kid 값을 key로 가지도록 구현
Id Token을 검증할 때, 매번 공개키 목록을 조회하면 + * 요청이 차단될 수 있으므로 캐싱하는 과정이 필요.
따라서 kid에 대한 Key를 찾을 수 없으면, fallback을 통해 캐시를 업데이트함
이때, 동시에 여러 요청이 들어오면 동시성 + * 문제가 발생할 수 있으므로 ReentrantLock을 사용하여 상호 배제 구현
데드락을 방지하기 위해 ReentrantLock.tryLock() 메서드를 사용하였음
또한 반드시 + * fallback에서 Timeout에 대한 예외 발생을 구현 해야함
존재하지 않는 kid로 계속 요청 시 fallback이 계속 호출되므로 공격 가능성이 있음.
+ * + * @param kid 캐시의 Key로 사용될 OpenId Id Token 헤더의 kid 값 + * @param fallback 캐시 미스 발생 시 캐시에 Key를 등록할 JwkSet을 반환하는 함수 + * @return 캐시 Hit의 경우 Key 반환, 캐시 Miss에서 fallback으로 반환된 JwkSet에 Key가 없으면 null 반환 + */ + @Nullable + public Key provide(String kid, Supplier fallback) { + Key key = cache.get(kid); + if (key != null) { + return key; + } + log.info("kid에 대한 OpenId Key를 찾지 못해 Key 목록 조회를 시도합니다. kid={}", kid); + try { + if (lock.tryLock(5, TimeUnit.SECONDS)) { + try { + key = cache.get(kid); + if (key != null) { + return key; + } + JwkSet jwkSet = fallback.get(); + jwkSet.forEach(jwk -> cache.put(jwk.getId(), jwk.toKey())); + key = cache.get(kid); + if (key == null) { + log.warn("OpenId kid에 대한 Key를 찾을 수 없습니다. kid={}", kid); + } + return key; + } finally { + lock.unlock(); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("스레드가 인터럽트 되었습니다.", e); + } + throw new UnexpectedException("OpenId Key를 가져오는 중, 락 대기로 인해 Key를 획득하지 못했습니다. kid=" + kid); + } +} diff --git a/backend/src/main/java/com/festago/auth/infrastructure/CachedOpenIdKeyProvider.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/CachedOpenIdKeyProvider.java similarity index 98% rename from backend/src/main/java/com/festago/auth/infrastructure/CachedOpenIdKeyProvider.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/CachedOpenIdKeyProvider.java index dbad61d73..14fc6feab 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/CachedOpenIdKeyProvider.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/CachedOpenIdKeyProvider.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.common.exception.UnexpectedException; import io.jsonwebtoken.security.JwkSet; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/FestagoOpenIdClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/FestagoOpenIdClient.java similarity index 96% rename from backend/src/main/java/com/festago/auth/infrastructure/FestagoOpenIdClient.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/FestagoOpenIdClient.java index 7c5d42ad9..836bb11dd 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/FestagoOpenIdClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/FestagoOpenIdClient.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.auth.domain.OpenIdClient; import com.festago.auth.domain.SocialType; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java similarity index 92% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdClient.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java index 16c532174..5c2fbd0be 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdClient.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.auth.domain.OpenIdClient; import com.festago.auth.domain.SocialType; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClient.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksClient.java similarity index 97% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClient.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksClient.java index 1c172c98d..43a86f38d 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClient.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksClient.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.InternalServerException; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksErrorHandler.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksErrorHandler.java similarity index 95% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksErrorHandler.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksErrorHandler.java index 3b2344aa5..8d17df3b0 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdJwksErrorHandler.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdJwksErrorHandler.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.InternalServerException; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdPublicKeyLocator.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdPublicKeyLocator.java similarity index 94% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdPublicKeyLocator.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdPublicKeyLocator.java index 80d1de87c..4e474f768 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdPublicKeyLocator.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdPublicKeyLocator.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.UnauthorizedException; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProvider.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdUserInfoProvider.java similarity index 98% rename from backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProvider.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdUserInfoProvider.java index deb1e61ae..30c81b05f 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProvider.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/KakaoOpenIdUserInfoProvider.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.auth.domain.OpenIdNonceValidator; import com.festago.auth.domain.OpenIdUserInfoProvider; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/NoopOpenIdNonceValidator.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/NoopOpenIdNonceValidator.java similarity index 91% rename from backend/src/main/java/com/festago/auth/infrastructure/NoopOpenIdNonceValidator.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/NoopOpenIdNonceValidator.java index e53fa5ac0..646bb4e5a 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/NoopOpenIdNonceValidator.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/NoopOpenIdNonceValidator.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.auth.domain.OpenIdNonceValidator; import java.util.Date; diff --git a/backend/src/main/java/com/festago/auth/infrastructure/OpenIdIdTokenParser.java b/backend/src/main/java/com/festago/auth/infrastructure/openid/OpenIdIdTokenParser.java similarity index 95% rename from backend/src/main/java/com/festago/auth/infrastructure/OpenIdIdTokenParser.java rename to backend/src/main/java/com/festago/auth/infrastructure/openid/OpenIdIdTokenParser.java index eadce84fc..24d85f0bf 100644 --- a/backend/src/main/java/com/festago/auth/infrastructure/OpenIdIdTokenParser.java +++ b/backend/src/main/java/com/festago/auth/infrastructure/openid/OpenIdIdTokenParser.java @@ -1,4 +1,4 @@ -package com.festago.auth.infrastructure; +package com.festago.auth.infrastructure.openid; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.UnauthorizedException; diff --git a/backend/src/main/java/com/festago/member/domain/Member.java b/backend/src/main/java/com/festago/member/domain/Member.java index e72a9c783..7c7624859 100644 --- a/backend/src/main/java/com/festago/member/domain/Member.java +++ b/backend/src/main/java/com/festago/member/domain/Member.java @@ -38,6 +38,7 @@ public class Member extends BaseTimeEntity { private static final String DEFAULT_IMAGE_URL = "https://festa-go.site/images/default-profile.png"; + private static final String DEFAULT_NICKNAME = "FestivalLover"; private static final int MAX_SOCIAL_ID_LENGTH = 255; private static final int MAX_NICKNAME_LENGTH = 30; private static final int MAX_PROFILE_IMAGE_LENGTH = 255; @@ -75,7 +76,7 @@ public Member(Long id, String socialId, SocialType socialType, String nickname, this.id = id; this.socialId = socialId; this.socialType = socialType; - this.nickname = nickname; + this.nickname = (StringUtils.hasText(nickname)) ? nickname : DEFAULT_NICKNAME; this.profileImage = (StringUtils.hasText(profileImage)) ? profileImage : DEFAULT_IMAGE_URL; } @@ -98,7 +99,6 @@ private void validateSocialType(SocialType socialType) { private void validateNickname(String nickname) { String fieldName = "nickname"; - Validator.notBlank(nickname, fieldName); Validator.maxLength(nickname, MAX_NICKNAME_LENGTH, fieldName); } diff --git a/backend/src/main/java/com/festago/upload/domain/UploadFile.java b/backend/src/main/java/com/festago/upload/domain/UploadFile.java index e2c82cbca..eae75de31 100644 --- a/backend/src/main/java/com/festago/upload/domain/UploadFile.java +++ b/backend/src/main/java/com/festago/upload/domain/UploadFile.java @@ -160,7 +160,7 @@ public MimeType getMimeType() { } public URI getUploadUri() { - return location.resolve(getName()); + return location.resolve("/" + getName()); } public String getName() { diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index eaa21d010..9974ebc34 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit eaa21d0104319544dfdfbca91a878781081379b3 +Subproject commit 9974ebc34f4ec43fd3163418a56d99001e825cfb diff --git a/backend/src/test/java/com/festago/auth/domain/OAuth2ClientsTest.java b/backend/src/test/java/com/festago/auth/domain/OAuth2ClientsTest.java index 4172552ba..dea7fe991 100644 --- a/backend/src/test/java/com/festago/auth/domain/OAuth2ClientsTest.java +++ b/backend/src/test/java/com/festago/auth/domain/OAuth2ClientsTest.java @@ -8,10 +8,10 @@ import com.festago.auth.application.OAuth2Client; import com.festago.auth.application.OAuth2Clients; import com.festago.auth.application.OAuth2Clients.OAuth2ClientsBuilder; -import com.festago.auth.infrastructure.FestagoOAuth2Client; -import com.festago.auth.infrastructure.KakaoOAuth2AccessTokenClient; -import com.festago.auth.infrastructure.KakaoOAuth2Client; -import com.festago.auth.infrastructure.KakaoOAuth2UserInfoClient; +import com.festago.auth.infrastructure.oauth2.FestagoOAuth2Client; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2AccessTokenClient; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2Client; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2UserInfoClient; import com.festago.common.exception.BadRequestException; import com.festago.common.exception.UnexpectedException; import org.junit.jupiter.api.DisplayNameGeneration; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/CachedOpenIdKeyProviderTest.java b/backend/src/test/java/com/festago/auth/infrastructure/CachedOpenIdKeyProviderTest.java index 304dab78d..c4b6cad68 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/CachedOpenIdKeyProviderTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/CachedOpenIdKeyProviderTest.java @@ -6,6 +6,7 @@ import static org.mockito.BDDMockito.times; import static org.mockito.BDDMockito.verify; +import com.festago.auth.infrastructure.openid.CachedOpenIdKeyProvider; import io.jsonwebtoken.security.JwkSet; import io.jsonwebtoken.security.Jwks; import java.security.Key; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClientTest.java b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClientTest.java index effb8cbe8..fd9b99077 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClientTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2AccessTokenClientTest.java @@ -7,7 +7,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.festago.auth.dto.KakaoAccessTokenResponse; -import com.festago.auth.infrastructure.KakaoOAuth2AccessTokenErrorHandler.KakaoOAuth2ErrorResponse; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2AccessTokenClient; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2AccessTokenErrorHandler.KakaoOAuth2ErrorResponse; import com.festago.common.exception.BadRequestException; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.InternalServerException; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClientTest.java b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClientTest.java index 12958a788..73b459a5e 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClientTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOAuth2UserInfoClientTest.java @@ -13,6 +13,7 @@ import com.festago.auth.dto.KakaoUserInfo; import com.festago.auth.dto.KakaoUserInfo.KakaoAccount; import com.festago.auth.dto.KakaoUserInfo.KakaoAccount.Profile; +import com.festago.auth.infrastructure.oauth2.KakaoOAuth2UserInfoClient; import com.festago.common.exception.BadRequestException; import com.festago.common.exception.InternalServerException; import org.junit.jupiter.api.DisplayNameGeneration; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClientTest.java b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClientTest.java index 06b5396db..58db2a900 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClientTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdJwksClientTest.java @@ -5,6 +5,7 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import com.fasterxml.jackson.databind.ObjectMapper; +import com.festago.auth.infrastructure.openid.KakaoOpenIdJwksClient; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.InternalServerException; import io.jsonwebtoken.Identifiable; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java index 7c242b072..0f03442c9 100644 --- a/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java +++ b/backend/src/test/java/com/festago/auth/infrastructure/KakaoOpenIdUserInfoProviderTest.java @@ -7,6 +7,9 @@ import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.spy; +import com.festago.auth.infrastructure.openid.KakaoOpenIdPublicKeyLocator; +import com.festago.auth.infrastructure.openid.KakaoOpenIdUserInfoProvider; +import com.festago.auth.infrastructure.openid.NoopOpenIdNonceValidator; import com.festago.common.exception.ErrorCode; import com.festago.common.exception.UnauthorizedException; import io.jsonwebtoken.Jwts; diff --git a/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java new file mode 100644 index 000000000..bd28c0965 --- /dev/null +++ b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdClientTest.java @@ -0,0 +1,190 @@ +package com.festago.auth.infrastructure.openid; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.UnauthorizedException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.time.Clock; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AppleOpenIdClientTest { + + AppleOpenIdClient appleOpenIdClient; + + AppleOpenIdPublicKeyLocator keyLocator; + + Clock clock; + + Key key = Keys.hmacShaKeyFor("key".repeat(15).getBytes(StandardCharsets.UTF_8)); + + @BeforeEach + void setUp() { + keyLocator = mock(); + clock = spy(Clock.systemDefaultZone()); + appleOpenIdClient = new AppleOpenIdClient( + "appleClientId", + keyLocator, + new NoopOpenIdNonceValidator(), + clock + ); + } + + @Test + void audience가_올바르지_않으면_예외() { + // given + given(keyLocator.locate(any())) + .willReturn(key); + String idToken = Jwts.builder() + .audience().add("wrong") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when & then + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) + .isInstanceOf(UnauthorizedException.class) + .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); + } + + @Test + void issuer가_올바르지_않으면_예외() { + // given + given(keyLocator.locate(any())) + .willReturn(key); + String idToken = Jwts.builder() + .audience().add("client-id") + .and() + .issuer("wrong") + .signWith(key) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when & then + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) + .isInstanceOf(UnauthorizedException.class) + .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); + } + + @Test + void 토큰이_만료되면_예외() { + // given + given(keyLocator.locate(any())) + .willReturn(key); + Date yesterday = Date.from(clock.instant().minus(1, ChronoUnit.DAYS)); + String idToken = Jwts.builder() + .audience().add("client-id") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .expiration(yesterday) + .compact(); + + // when & then + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) + .isInstanceOf(UnauthorizedException.class) + .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); + } + + @Test + void 토큰에_서명된_키가_파싱할때_키와_일치하지_않으면_예외() { + // given + Key otherKey = Keys.hmacShaKeyFor("otherKey".repeat(10).getBytes(StandardCharsets.UTF_8)); + given(keyLocator.locate(any())) + .willReturn(otherKey); + String idToken = Jwts.builder() + .audience().add("client-id") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when & then + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) + .isInstanceOf(UnauthorizedException.class) + .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); + } + + @Test + void 파싱할때_키가_null이면_예외() { + // given + given(keyLocator.locate(any())) + .willReturn(null); + String idToken = Jwts.builder() + .audience().add("client-id") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when & then + assertThatThrownBy(() -> appleOpenIdClient.getUserInfo(idToken)) + .isInstanceOf(UnauthorizedException.class) + .hasMessage(ErrorCode.OPEN_ID_INVALID_TOKEN.getMessage()); + } + + @Test + void audience_issuer가_올바르면_성공() { + // given + String socialId = "12345"; + given(keyLocator.locate(any())) + .willReturn(key); + String idToken = Jwts.builder() + .audience().add("appleClientId") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .subject(socialId) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when + var expect = appleOpenIdClient.getUserInfo(idToken); + + // then + assertThat(expect.socialId()).isEqualTo(socialId); + } + + @Test + void audience_값은_apple_client_id_와_같으면_성공() { + // given + String socialId = "12345"; + given(keyLocator.locate(any())) + .willReturn(key); + String idToken = Jwts.builder() + .audience().add("appleClientId") + .and() + .issuer("https://appleid.apple.com") + .signWith(key) + .subject(socialId) + .expiration(Date.from(clock.instant().plus(1, ChronoUnit.DAYS))) + .compact(); + + // when + var expect = appleOpenIdClient.getUserInfo(idToken); + + // then + assertThat(expect.socialId()).isEqualTo(socialId); + } + + +} diff --git a/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClientTest.java b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClientTest.java new file mode 100644 index 000000000..559721c81 --- /dev/null +++ b/backend/src/test/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClientTest.java @@ -0,0 +1,111 @@ +package com.festago.auth.infrastructure.openid; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.InternalServerException; +import io.jsonwebtoken.Identifiable; +import io.jsonwebtoken.security.JwkSet; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.response.MockRestResponseCreators; + +@DisplayNameGeneration(ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +@RestClientTest(AppleOpenIdJwksClient.class) +class AppleOpenIdJwksClientTest { + + private static final String URL = "https://appleid.apple.com/auth/keys"; + + @Autowired + AppleOpenIdJwksClient appleOpenIdJwksClient; + + @Autowired + MockRestServiceServer mockServer; + + @Autowired + ObjectMapper objectMapper; + + @Test + void 상태코드가_4xx이면_InternalServer_예외() { + // given + mockServer.expect(requestTo(URL)) + .andRespond(MockRestResponseCreators.withBadRequest() + .contentType(MediaType.APPLICATION_JSON)); + + // when & then + assertThatThrownBy(() -> appleOpenIdJwksClient.requestGetJwks()) + .isInstanceOf(InternalServerException.class) + .hasMessage(ErrorCode.OPEN_ID_PROVIDER_NOT_RESPONSE.getMessage()); + } + + @Test + void 상태코드가_5xx이면_InternalServer_예외() { + // given + mockServer.expect(requestTo(URL)) + .andRespond(MockRestResponseCreators.withServerError() + .contentType(MediaType.APPLICATION_JSON)); + + // when & then + assertThatThrownBy(() -> appleOpenIdJwksClient.requestGetJwks()) + .isInstanceOf(InternalServerException.class) + .hasMessage(ErrorCode.OPEN_ID_PROVIDER_NOT_RESPONSE.getMessage()); + } + + @Test + void 성공() { + // given + String jwksJson = """ + { + "keys": [ + { + "kty": "RSA", + "kid": "pyaRQpAbnY", + "use": "sig", + "alg": "RS256", + "n": "qHiwOpizi6xHG8FIOSWH4l0P1CjLIC7aBFkhbk7BrD4s9KQAs5Sj5xAtOwlZMyP2XFcqRtZBLIMM7vw_CNERtRrhc68se5hQE_vsrHy7ugcQU6ogJS6s54zqO-zTUfaa3mABM6iR-EfgSpvz33WTQZAPtwAyxaSLknHyDzWjHEZ44WqaQBdcMAvgsWMYG5dBfnV-3Or3V2r1vdbinRE5NomE2nsKDbnJ3yo3u-x9TizKazS1JV3umt71xDqbruZLybIrimrzg_i9OSIzT2o5ZWz8zdYkKHZ4cvRPh-DDt8kV7chzR2tenPF2c5WXuK-FumOrjT7WW6uwSvhnhwNZuw", + "e": "AQAB" + }, + { + "kty": "RSA", + "kid": "lVHdOx8ltR", + "use": "sig", + "alg": "RS256", + "n": "nXDu9MPf6dmVtFbDdAaal_0cO9ur2tqrrmCZaAe8TUWHU8AprhJG4DaQoCIa4UsOSCbCYOjPpPGGdE_p0XeP1ew55pBIquNhNtNNEMX0jNYAKcA9WAP1zGSkvH5m39GMFc4SsGiQ_8Szht9cayJX1SJALEgSyDOFLs-ekHnexqsr-KPtlYciwer5jaNcW3B7f9VNp1XCypQloQwSGVismPHwDJowPQ1xOWmhBLCK50NV38ZjobUDSBbCeLYecMtsdL5ZGv-iufddBh3RHszQiD2G-VXoGOs1yE33K4uAto2F2bHVcKOUy0__9qEsXZGf-B5ZOFucUkoN7T2iqu2E2Q", + "e": "AQAB" + }, + { + "kty": "RSA", + "kid": "Bh6H7rHVmb", + "use": "sig", + "alg": "RS256", + "n": "2HkIZ7xKMUYH_wtt2Gwq6jXKRl-Ng5vdwd-XcWn5RIW82-uxdmGJyTo3T6MPty-xWUeW7FCs9NlM4yu02GKgwep7TKfnOovP78sd3rESbZsvuN7zD_Vk6aZP7QfqblElUtiMQxh9bu-gZUeMZfa_ndX-P5C4yKtZvXGrSPLLjyAcSmSHNLZnWbZXjeIVsgXWHMr5fwVEAkftHq_4py82xgn2XEK0FK9HmWOCZ47Wcx9fWBnqSi9JTJTUX0lh-kI5TcYfv9UKX2oe3uyOn-A460E_L_4ximlM-lgi3otw26EZfAGY9FFgSZoACjhgw_z5NRbK9dycHRpeLY9GxIyiYw", + "e": "AQAB" + } + ] + } + """; + mockServer.expect(requestTo(URL)) + .andRespond(MockRestResponseCreators.withSuccess() + .body(jwksJson) + .contentType(MediaType.APPLICATION_JSON)); + + // when + JwkSet actual = appleOpenIdJwksClient.requestGetJwks(); + + // then + assertThat(actual.getKeys()) + .map(Identifiable::getId) + .containsExactlyInAnyOrder("pyaRQpAbnY", "lVHdOx8ltR", "Bh6H7rHVmb"); + } + + +} diff --git a/backend/src/test/java/com/festago/member/domain/MemberTest.java b/backend/src/test/java/com/festago/member/domain/MemberTest.java index 43ccf576e..43342b7cd 100644 --- a/backend/src/test/java/com/festago/member/domain/MemberTest.java +++ b/backend/src/test/java/com/festago/member/domain/MemberTest.java @@ -55,10 +55,12 @@ class MemberTest { @ParameterizedTest @NullSource @ValueSource(strings = {"", " ", "\t", "\n"}) - void nickname이_null_또는_공백이면_예외(String nickname) { - // when & then - assertThatThrownBy(() -> new Member(1L, "12345", SocialType.FESTAGO, nickname, "profileImage.png")) - .isInstanceOf(ValidException.class); + void nickname이_null_또는_공백이면_기본_닉네임_생성(String nickname) { + // given && when + Member member = new Member(1L, "12345", SocialType.FESTAGO, nickname, "profileImage.png"); + + // then + assertThat(member.getNickname()).isEqualTo("FestivalLover"); } @Test diff --git a/backend/src/test/resources/application-test.yml b/backend/src/test/resources/application-test.yml index c8aed0b4e..d5f429a11 100644 --- a/backend/src/test/resources/application-test.yml +++ b/backend/src/test/resources/application-test.yml @@ -21,6 +21,8 @@ festago: auth-secret-key: festagofestagofestagofestagofestagofestagofestagofestagofestagofestagofestagofestagofestagofestago cors-allow-origins: http://localhost:3000 oauth2: + apple: + client-id: appleClientId kakao: grant-type: grant-type rest-api-key: rest-api-key