Skip to content

Commit c76fc7d

Browse files
committed
fix: authenticate Apple users without email
1 parent 4043ab2 commit c76fc7d

4 files changed

Lines changed: 80 additions & 2 deletions

File tree

ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public void saveAuthentication(User myUser) {
125125
}
126126

127127
UserDetails userDetailsUser = org.springframework.security.core.userdetails.User.builder()
128-
.username(myUser.getEmail())
128+
.username(authenticationName(myUser))
129129
.password(password)
130130
.roles(myUser.getRole().name())
131131
.build();
@@ -137,6 +137,14 @@ public void saveAuthentication(User myUser) {
137137
SecurityContextHolder.getContext().setAuthentication(authentication);
138138
}
139139

140+
private String authenticationName(User user) {
141+
String email = user.getEmail();
142+
if (email != null && !email.isBlank()) {
143+
return email;
144+
}
145+
return "user:" + user.getId();
146+
}
147+
140148
// 비밀번호 랜덤 생성
141149
private String generateRandomPassword(int length) {
142150
final String CHAR_SET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+[]{}|;:,.<>?";

ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginFilter.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,13 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
8181
}
8282

8383
String appleUserId = tokenClaims.getSubject();
84+
String email = firstNonBlank(
85+
oAuthAppleRequestDto.getEmail(),
86+
tokenClaims.get("email", String.class)
87+
);
8488
String appleRefreshToken = exchangeAppleRefreshToken(oAuthAppleRequestDto);
8589

86-
OAuthAppleUserDto oAuthAppleUserDto = new OAuthAppleUserDto(appleUserId, oAuthAppleRequestDto.getEmail(), oAuthAppleRequestDto.getFullName());
90+
OAuthAppleUserDto oAuthAppleUserDto = new OAuthAppleUserDto(appleUserId, email, oAuthAppleRequestDto.getFullName());
8791

8892
Optional<User> existingUser = userRepository.findBySocialTypeAndSocialId(SocialType.APPLE, appleUserId);
8993

@@ -111,6 +115,16 @@ private String exchangeAppleRefreshToken(OAuthAppleRequestDto oAuthAppleRequestD
111115
}
112116
}
113117

118+
private String firstNonBlank(String first, String second) {
119+
if (first != null && !first.isBlank()) {
120+
return first;
121+
}
122+
if (second != null && !second.isBlank()) {
123+
return second;
124+
}
125+
return null;
126+
}
127+
114128
@Override
115129
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
116130
AuthenticationException failed) throws IOException {

ontime-back/src/test/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilterTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,19 @@ void socialLoginUserWithoutPasswordReceivesGeneratedAuthenticationPassword() {
168168
.containsExactly("ROLE_USER");
169169
}
170170

171+
@Test
172+
void socialLoginUserWithoutEmailUsesUserIdAuthenticationName() {
173+
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(mock(JwtTokenProvider.class), mock(UserRepository.class));
174+
User user = user(null, null);
175+
176+
filter.saveAuthentication(user);
177+
178+
assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("user:1");
179+
assertThat(SecurityContextHolder.getContext().getAuthentication().getAuthorities())
180+
.extracting("authority")
181+
.containsExactly("ROLE_USER");
182+
}
183+
171184
private User user(String email, String password) {
172185
return User.builder()
173186
.id(1L)

ontime-back/src/test/java/devkor/ontime_back/global/oauth/OAuthLoginFilterValidationTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
55
import devkor.ontime_back.dto.AppleTokenResponseDto;
6+
import devkor.ontime_back.dto.OAuthAppleUserDto;
67
import devkor.ontime_back.global.jwt.JwtTokenProvider;
78
import devkor.ontime_back.global.oauth.apple.AppleLoginFilter;
89
import devkor.ontime_back.global.oauth.apple.AppleLoginService;
@@ -400,6 +401,38 @@ void appleLoginFilterRegistersNewUser() throws Exception {
400401
verify(appleLoginService).handleRegister(any(), any(), any());
401402
}
402403

404+
@Test
405+
@DisplayName("애플 로그인 필터가 요청 email이 비어 있으면 identity token email로 신규 유저를 생성한다")
406+
void appleLoginFilterUsesIdentityTokenEmailWhenRequestEmailIsMissing() throws Exception {
407+
AppleLoginFilter filter = new AppleLoginFilter(
408+
"/oauth2/apple/login",
409+
objectMapper,
410+
validator,
411+
appleLoginService,
412+
userRepository);
413+
MockHttpServletResponse response = new MockHttpServletResponse();
414+
Claims claims = Jwts.claims().setSubject("apple-id");
415+
claims.put("email", "token@example.com");
416+
AppleTokenResponseDto tokenResponse = appleTokenResponse("apple-refresh-token");
417+
User newUser = user(2L, "token@example.com", Role.GUEST);
418+
419+
when(appleLoginService.verifyIdentityToken("apple-id-token")).thenReturn(claims);
420+
when(appleLoginService.getAppleAccessTokenAndRefreshToken("auth-code")).thenReturn(tokenResponse);
421+
when(userRepository.findBySocialTypeAndSocialId(SocialType.APPLE, "apple-id"))
422+
.thenReturn(Optional.empty());
423+
when(appleLoginService.handleRegister(eq("apple-refresh-token"), any(), any())).thenReturn(
424+
new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(newUser, null)
425+
);
426+
427+
assertThat(filter.attemptAuthentication(
428+
request("/oauth2/apple/login", appleBodyWithoutEmail()),
429+
response).getPrincipal()).isSameAs(newUser);
430+
431+
verify(appleLoginService).handleRegister(eq("apple-refresh-token"), org.mockito.ArgumentMatchers.argThat(
432+
(OAuthAppleUserDto userDto) -> "token@example.com".equals(userDto.getEmail())
433+
), eq(response));
434+
}
435+
403436
@Test
404437
@DisplayName("애플 로그인 필터가 Apple refresh token 교환 실패 시 identity token 검증만으로 신규 유저를 회원가입 처리한다")
405438
void appleLoginFilterRegistersNewUserWhenTokenExchangeFails() throws Exception {
@@ -478,6 +511,16 @@ private String validAppleBody() {
478511
""";
479512
}
480513

514+
private String appleBodyWithoutEmail() {
515+
return """
516+
{
517+
"idToken": "apple-id-token",
518+
"authCode": "auth-code",
519+
"fullName": "Apple User"
520+
}
521+
""";
522+
}
523+
481524
private AppleTokenResponseDto appleTokenResponse(String refreshToken) {
482525
AppleTokenResponseDto response = new AppleTokenResponseDto();
483526
ReflectionTestUtils.setField(response, "refreshToken", refreshToken);

0 commit comments

Comments
 (0)