-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [STMT-146] ✨ 애플 로그인 기능 추가 * [STMT-146] 🐛 AccessToken의 Bearer 파싱하여 애플 로그인 검증 하도록 변경 * [STMT-146] 🐛 Access Token에서 kid 값을 Jackson을 이용하여 추출
- Loading branch information
Showing
9 changed files
with
145 additions
and
13 deletions.
There are no files selected for viewing
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
62 changes: 62 additions & 0 deletions
62
src/main/java/com/stumeet/server/common/client/oauth/apple/AppleIdTokenProvider.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,62 @@ | ||
package com.stumeet.server.common.client.oauth.apple; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.stumeet.server.common.client.oauth.apple.model.ApplePublicKeyResponses; | ||
import io.jsonwebtoken.Jwts; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.math.BigInteger; | ||
import java.security.KeyFactory; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PublicKey; | ||
import java.security.spec.InvalidKeySpecException; | ||
import java.security.spec.RSAPublicKeySpec; | ||
import java.util.Base64; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class AppleIdTokenProvider { | ||
private final ObjectMapper objectMapper; | ||
|
||
public PublicKey getSecretKey(ApplePublicKeyResponses publicKeys, String accessToken) { | ||
String kid = extractKid(accessToken); | ||
|
||
ApplePublicKeyResponses.ApplePublicKeyResponse applePublicKey = publicKeys.keys().stream() | ||
.filter(key -> key.kid().equals(kid)) | ||
.findAny() | ||
.orElseThrow(() -> new IllegalArgumentException("매칭되는 kid가 존재하지 않습니다.")); | ||
|
||
BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(applePublicKey.n())); | ||
BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(applePublicKey.e())); | ||
|
||
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent); | ||
try { | ||
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); | ||
|
||
return keyFactory.generatePublic(keySpec); | ||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private String extractKid(String accessToken) { | ||
String[] splitToken = accessToken.split("\\."); | ||
String header = new String(Base64.getUrlDecoder().decode(splitToken[0])); | ||
try { | ||
return objectMapper.readTree(header).get("kid").asText(); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
|
||
public String extractUserId(PublicKey publicKey, String idToken) { | ||
return Jwts.parser() | ||
.verifyWith(publicKey) | ||
.build() | ||
.parseSignedClaims(idToken) | ||
.getPayload() | ||
.getSubject(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/com/stumeet/server/common/client/oauth/apple/AppleOAuthClient.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,28 @@ | ||
package com.stumeet.server.common.client.oauth.apple; | ||
|
||
import com.stumeet.server.common.client.oauth.OAuthClient; | ||
import com.stumeet.server.common.client.oauth.apple.model.ApplePublicKeyResponses; | ||
import com.stumeet.server.common.client.oauth.model.OAuthUserProfileResponse; | ||
import com.stumeet.server.common.util.JwtUtil; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.security.PublicKey; | ||
|
||
@Component(value = "apple") | ||
@RequiredArgsConstructor | ||
public class AppleOAuthClient implements OAuthClient { | ||
|
||
private final AppleOAuthFeignClient appleOAuthClient; | ||
private final AppleIdTokenProvider appleIdTokenProvider; | ||
|
||
@Override | ||
public OAuthUserProfileResponse getUserId(String accessToken) { | ||
ApplePublicKeyResponses publicKeys = appleOAuthClient.getPublicKeys(); | ||
String parseAccessToken = JwtUtil.resolveToken(accessToken); | ||
PublicKey publicKey = appleIdTokenProvider.getSecretKey(publicKeys, parseAccessToken); | ||
String id = appleIdTokenProvider.extractUserId(publicKey, parseAccessToken); | ||
|
||
return new OAuthUserProfileResponse(id); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/com/stumeet/server/common/client/oauth/apple/AppleOAuthFeignClient.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,13 @@ | ||
package com.stumeet.server.common.client.oauth.apple; | ||
|
||
import com.stumeet.server.common.client.oauth.apple.model.ApplePublicKeyResponses; | ||
import org.springframework.cloud.openfeign.FeignClient; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
|
||
@FeignClient(name = "appleOAuthClient", url = "https://appleid.apple.com") | ||
public interface AppleOAuthFeignClient { | ||
|
||
@GetMapping("/auth/keys") | ||
ApplePublicKeyResponses getPublicKeys(); | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
...main/java/com/stumeet/server/common/client/oauth/apple/model/ApplePublicKeyResponses.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,16 @@ | ||
package com.stumeet.server.common.client.oauth.apple.model; | ||
|
||
import java.util.List; | ||
|
||
public record ApplePublicKeyResponses( | ||
List<ApplePublicKeyResponse> keys | ||
|
||
) { | ||
public record ApplePublicKeyResponse( | ||
String kid, | ||
String n, | ||
String e | ||
) { | ||
} | ||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.stumeet.server.common.util; | ||
|
||
public class JwtUtil { | ||
|
||
private JwtUtil() { | ||
|
||
} | ||
|
||
public static String resolveToken(String token) { | ||
if (token != null && token.startsWith("Bearer ")) { | ||
return token.substring(7); | ||
} | ||
return null; | ||
} | ||
|
||
} |