Skip to content

Commit e8d7533

Browse files
Merge pull request #65 from DevKor-github/develop
Apple 로그인 시 Apple로부터 받은 리프레시 토큰을 안전하게 저장
2 parents 70e23db + 1f7ad69 commit e8d7533

24 files changed

Lines changed: 324 additions & 71 deletions

.github/workflows/deploy-to-dev-ec2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
KAKAO_CLIENT_ID="${{ secrets.KAKAO_CLIENT_ID }}" \
7777
KAKAO_REDIRECT_URI="${{ secrets.KAKAO_REDIRECT_URI }}" \
7878
KAKAO_CLIENT_SECRET="${{ secrets.KAKAO_CLIENT_SECRET }}" \
79+
KMS_KEY_ID="${{ secrets.KMS_KEY_ID }}" \
7980
nohup java -Duser.timezone=Asia/Seoul -jar $JAR_NAME --spring.profiles.active=dev > app.log 2>&1 &
8081
8182
echo "✅ 배포 완료"

.github/workflows/deploy-to-prod-ec2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
KAKAO_CLIENT_ID="${{ secrets.KAKAO_CLIENT_ID }}" \
7777
KAKAO_REDIRECT_URI="${{ secrets.KAKAO_REDIRECT_URI }}" \
7878
KAKAO_CLIENT_SECRET="${{ secrets.KAKAO_CLIENT_SECRET }}" \
79+
KMS_KEY_ID="${{ secrets.KMS_KEY_ID }}" \
7980
nohup java -Duser.timezone=Asia/Seoul -jar $JAR_NAME --spring.profiles.active=prod > app.log 2>&1 &
8081
8182
echo "✅ 배포 완료"

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ dependencies {
6262
implementation platform('software.amazon.awssdk:bom:2.27.21')
6363
implementation 'software.amazon.awssdk:s3'
6464

65+
// AWS KMS
66+
implementation 'software.amazon.awssdk:kms'
67+
6568
// JWT 발급 및 검증
6669
implementation 'com.auth0:java-jwt:4.5.0'
6770

commitlint.config.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,7 @@ module.exports = {
2323
"db"
2424
]],
2525
"footer-empty": [2, "always"],
26-
"subject-case": [2, "always", [
27-
"lower-case", // lower case
28-
"upper-case", // UPPERCASE
29-
"camel-case", // camelCase
30-
"kebab-case", // kebab-case
31-
"pascal-case", // PascalCase
32-
"sentence-case", // Sentence case
33-
"snake-case", // snake_case
34-
"start-case", // Start Case
35-
]]
26+
"subject-case": [0]
3627
},
3728
prompt: {
3829
settings: {},

src/main/java/org/devkor/apu/saerok_server/domain/auth/application/AbstractSocialAuthService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212
import org.devkor.apu.saerok_server.domain.auth.core.service.UserProvisioningService;
1313
import org.devkor.apu.saerok_server.domain.auth.infra.SocialAuthClient;
1414
import org.devkor.apu.saerok_server.domain.auth.core.dto.SocialUserInfo;
15+
import org.devkor.apu.saerok_server.global.security.crypto.DataCryptoService;
16+
import org.devkor.apu.saerok_server.global.security.crypto.EncryptedPayload;
1517
import org.devkor.apu.saerok_server.global.util.dto.ClientInfo;
1618
import org.springframework.http.HttpHeaders;
1719
import org.springframework.http.ResponseEntity;
1820

21+
import java.nio.charset.StandardCharsets;
22+
1923
@Slf4j
2024
@RequiredArgsConstructor
2125
@Transactional
@@ -24,6 +28,7 @@ public abstract class AbstractSocialAuthService {
2428
private final SocialAuthRepository socialAuthRepository;
2529
private final AuthTokenFacade authTokenFacade;
2630
private final UserProvisioningService userProvisioningService;
31+
private final DataCryptoService dataCryptoService;
2732

2833
protected abstract SocialAuthClient client();
2934

@@ -37,6 +42,12 @@ public ResponseEntity<AccessTokenResponse> authenticate(String authorizationCode
3742
.findByProviderAndProviderUserId(provider, userInfo.sub())
3843
.orElseGet(() -> userProvisioningService.provisionNewUser(provider, userInfo));
3944

45+
if (userInfo.refreshToken() != null) {
46+
log.info("소셜 공급자로부터 받은 리프레시 토큰: {}", userInfo.refreshToken());
47+
EncryptedPayload payload = dataCryptoService.encrypt(userInfo.refreshToken().getBytes(StandardCharsets.UTF_8));
48+
socialAuth.setRefreshToken(payload);
49+
}
50+
4051
AuthBundle bundle = authTokenFacade.issueTokens(
4152
socialAuth.getUser(),
4253
ci

src/main/java/org/devkor/apu/saerok_server/domain/auth/application/AppleAuthService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.devkor.apu.saerok_server.domain.auth.core.service.UserProvisioningService;
66
import org.devkor.apu.saerok_server.domain.auth.infra.AppleAuthClient;
77
import org.devkor.apu.saerok_server.domain.auth.infra.SocialAuthClient;
8+
import org.devkor.apu.saerok_server.global.security.crypto.DataCryptoService;
89
import org.springframework.stereotype.Service;
910

1011
@Service
@@ -16,9 +17,10 @@ public AppleAuthService(
1617
SocialAuthRepository socialAuthRepository,
1718
AuthTokenFacade authTokenFacade,
1819
UserProvisioningService userProvisioningService,
20+
DataCryptoService dataCryptoService,
1921
AppleAuthClient appleAuthClient
2022
) {
21-
super(socialAuthRepository, authTokenFacade, userProvisioningService);
23+
super(socialAuthRepository, authTokenFacade, userProvisioningService, dataCryptoService);
2224
this.appleAuthClient = appleAuthClient;
2325
}
2426

src/main/java/org/devkor/apu/saerok_server/domain/auth/application/KakaoAuthService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.devkor.apu.saerok_server.domain.auth.core.service.UserProvisioningService;
66
import org.devkor.apu.saerok_server.domain.auth.infra.KakaoAuthClient;
77
import org.devkor.apu.saerok_server.domain.auth.infra.SocialAuthClient;
8+
import org.devkor.apu.saerok_server.global.security.crypto.DataCryptoService;
89
import org.springframework.stereotype.Service;
910

1011
@Service
@@ -16,9 +17,10 @@ public KakaoAuthService(
1617
SocialAuthRepository socialAuthRepository,
1718
AuthTokenFacade authTokenFacade,
1819
UserProvisioningService userProvisioningService,
20+
DataCryptoService dataCryptoService,
1921
KakaoAuthClient kakaoAuthClient
2022
) {
21-
super(socialAuthRepository, authTokenFacade, userProvisioningService);
23+
super(socialAuthRepository, authTokenFacade, userProvisioningService, dataCryptoService);
2224
this.kakaoAuthClient = kakaoAuthClient;
2325
}
2426

src/main/java/org/devkor/apu/saerok_server/domain/auth/core/dto/SocialUserInfo.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
public record SocialUserInfo(
44
String sub,
5-
String email
5+
String email,
6+
String refreshToken
67
) {
78
}

src/main/java/org/devkor/apu/saerok_server/domain/auth/core/entity/SocialAuth.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
import lombok.AccessLevel;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7-
import lombok.Setter;
87
import org.devkor.apu.saerok_server.domain.user.core.entity.User;
98
import org.devkor.apu.saerok_server.global.entity.CreatedAtOnly;
10-
11-
import java.time.OffsetDateTime;
9+
import org.devkor.apu.saerok_server.global.security.crypto.EncryptedPayload;
1210

1311
@Entity
1412
@Table(
@@ -33,11 +31,24 @@ public class SocialAuth extends CreatedAtOnly {
3331
@Column(name = "provider_user_id", nullable = false)
3432
private String providerUserId;
3533

34+
@Embedded
35+
private SocialAuthRefreshToken refreshToken;
36+
3637
public static SocialAuth createSocialAuth(User user, SocialProviderType provider, String providerUserId) {
3738
SocialAuth socialAuth = new SocialAuth();
3839
socialAuth.user = user;
3940
socialAuth.provider = provider;
4041
socialAuth.providerUserId = providerUserId;
4142
return socialAuth;
4243
}
44+
45+
public void setRefreshToken(EncryptedPayload p) {
46+
47+
if (refreshToken == null) refreshToken = new SocialAuthRefreshToken();
48+
49+
refreshToken.setCiphertext(p.ciphertext());
50+
refreshToken.setKey(p.encryptedDataKey());
51+
refreshToken.setIv(p.iv());
52+
refreshToken.setTag(p.authTag());
53+
}
4354
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.devkor.apu.saerok_server.domain.auth.core.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Data;
5+
6+
/**
7+
* 소셜 공급자로부터 받은 리프레시 토큰을 AES-256-GCM 방식으로 양방향 암호화해서 관리
8+
*/
9+
@Embeddable
10+
@Data
11+
public class SocialAuthRefreshToken {
12+
13+
@Basic(fetch = FetchType.LAZY)
14+
@Column(name = "refresh_token_ciphertext", columnDefinition = "BYTEA")
15+
private byte[] ciphertext;
16+
17+
@Basic(fetch = FetchType.LAZY)
18+
@Column(name = "refresh_token_key", columnDefinition = "BYTEA")
19+
private byte[] key;
20+
21+
@Basic(fetch = FetchType.LAZY)
22+
@Column(name = "refresh_token_iv", columnDefinition = "BYTEA")
23+
private byte[] iv;
24+
25+
@Basic(fetch = FetchType.LAZY)
26+
@Column(name = "refresh_token_tag", columnDefinition = "BYTEA")
27+
private byte[] tag;
28+
29+
30+
}

0 commit comments

Comments
 (0)