-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* 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 <[email protected]>
- Loading branch information
1 parent
15cc097
commit 7b3c3eb
Showing
33 changed files
with
564 additions
and
28 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
...h/infrastructure/FestagoOAuth2Client.java → ...structure/oauth2/FestagoOAuth2Client.java
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
2 changes: 1 addition & 1 deletion
2
...ructure/KakaoOAuth2AccessTokenClient.java → .../oauth2/KakaoOAuth2AccessTokenClient.java
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
2 changes: 1 addition & 1 deletion
2
...e/KakaoOAuth2AccessTokenErrorHandler.java → ...2/KakaoOAuth2AccessTokenErrorHandler.java
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
2 changes: 1 addition & 1 deletion
2
...uth/infrastructure/KakaoOAuth2Client.java → ...rastructure/oauth2/KakaoOAuth2Client.java
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
2 changes: 1 addition & 1 deletion
2
...astructure/KakaoOAuth2UserInfoClient.java → ...ure/oauth2/KakaoOAuth2UserInfoClient.java
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
2 changes: 1 addition & 1 deletion
2
...ture/KakaoOAuth2UserInfoErrorHandler.java → ...uth2/KakaoOAuth2UserInfoErrorHandler.java
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
67 changes: 67 additions & 0 deletions
67
backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdClient.java
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,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<String> 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; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
backend/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksClient.java
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,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<JwkSet> 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); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...end/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdJwksErrorHandler.java
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,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); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...end/src/main/java/com/festago/auth/infrastructure/openid/AppleOpenIdPublicKeyLocator.java
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,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<Key> { | ||
|
||
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); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
...nd/src/main/java/com/festago/auth/infrastructure/openid/CachedAppleOpenIdKeyProvider.java
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,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<String, Key> cache = new HashMap<>(); | ||
private final ReentrantLock lock = new ReentrantLock(); | ||
|
||
/** | ||
* OpenId Key를 캐싱하여 반환하는 클래스 <br/> OpenID Id Token 헤더의 kid 값을 key로 가지도록 구현 <br/> Id Token을 검증할 때, 매번 공개키 목록을 조회하면 | ||
* 요청이 차단될 수 있으므로 캐싱하는 과정이 필요. <br/> 따라서 kid에 대한 Key를 찾을 수 없으면, fallback을 통해 캐시를 업데이트함 <br/> 이때, 동시에 여러 요청이 들어오면 동시성 | ||
* 문제가 발생할 수 있으므로 ReentrantLock을 사용하여 상호 배제 구현 <br/> 데드락을 방지하기 위해 ReentrantLock.tryLock() 메서드를 사용하였음 <br/> 또한 반드시 | ||
* fallback에서 Timeout에 대한 예외 발생을 구현 해야함<br/> 존재하지 않는 kid로 계속 요청 시 fallback이 계속 호출되므로 공격 가능성이 있음. <br/> | ||
* | ||
* @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<JwkSet> 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); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...frastructure/CachedOpenIdKeyProvider.java → ...cture/openid/CachedOpenIdKeyProvider.java
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
2 changes: 1 addition & 1 deletion
2
...h/infrastructure/FestagoOpenIdClient.java → ...structure/openid/FestagoOpenIdClient.java
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
2 changes: 1 addition & 1 deletion
2
...uth/infrastructure/KakaoOpenIdClient.java → ...rastructure/openid/KakaoOpenIdClient.java
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
2 changes: 1 addition & 1 deletion
2
...infrastructure/KakaoOpenIdJwksClient.java → ...ructure/openid/KakaoOpenIdJwksClient.java
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
2 changes: 1 addition & 1 deletion
2
...tructure/KakaoOpenIdJwksErrorHandler.java → ...e/openid/KakaoOpenIdJwksErrorHandler.java
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
2 changes: 1 addition & 1 deletion
2
...tructure/KakaoOpenIdPublicKeyLocator.java → ...e/openid/KakaoOpenIdPublicKeyLocator.java
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
2 changes: 1 addition & 1 deletion
2
...tructure/KakaoOpenIdUserInfoProvider.java → ...e/openid/KakaoOpenIdUserInfoProvider.java
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
2 changes: 1 addition & 1 deletion
2
...rastructure/NoopOpenIdNonceValidator.java → ...ture/openid/NoopOpenIdNonceValidator.java
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
2 changes: 1 addition & 1 deletion
2
...h/infrastructure/OpenIdIdTokenParser.java → ...structure/openid/OpenIdIdTokenParser.java
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
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
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
Submodule config
updated
from eaa21d to 9974eb
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
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
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
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.