Skip to content

Commit

Permalink
merge: 변경사항 반영
Browse files Browse the repository at this point in the history
  • Loading branch information
ksk0605 committed Oct 14, 2024
2 parents c171766 + 89e380d commit 06c3cc1
Show file tree
Hide file tree
Showing 21 changed files with 160 additions and 38 deletions.
3 changes: 3 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ dependencies {
testAnnotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.awaitility:awaitility:4.2.0'

// jackson
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'

// notification
implementation 'com.google.firebase:firebase-admin:9.3.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,47 @@
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mouda.backend.auth.business.result.LoginProcessResult;
import mouda.backend.auth.implement.AppleOauthManager;
import mouda.backend.auth.implement.LoginManager;
import mouda.backend.auth.implement.jwt.AccessTokenProvider;
import mouda.backend.auth.presentation.request.AppleOauthRequest;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.member.domain.LoginDetail;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
import mouda.backend.member.implement.MemberFinder;
import mouda.backend.member.implement.MemberWriter;

@Slf4j
@Service
@RequiredArgsConstructor
public class AppleAuthService {

private final AppleOauthManager oauthManager;
private final LoginManager loginManager;
private final MemberFinder memberFinder;
private final MemberWriter memberWriter;
private final AppleOauthManager appleOauthManager;
private final AccessTokenProvider accessTokenProvider;

public LoginResponse oauthLogin(AppleOauthRequest oauthRequest) {
String socialLoginId = oauthManager.getSocialLoginId(oauthRequest.code());
Member member = memberFinder.findByNonce(oauthRequest.nonce());
// TODO: 사용자 전환 로직 실행 이전부터 애플 소셜 회원가입이 진행되어있음. 현재 카카오 사용자가 전환을 시도하여 애플 로그인하면 같은 애플 로그인 사용자가 두 명이 되면서 에러가 터질 것.
if (oauthRequest.memberId() != null) {
String accessToken = loginManager.updateOauth(oauthRequest.memberId(), OauthType.APPLE, socialLoginId);
String accessToken = loginManager.updateOauth(oauthRequest.memberId(), OauthType.APPLE,
member.getSocialLoginId());
return new LoginResponse(accessToken);
}
LoginProcessResult loginProcessResult = loginManager.processSocialLogin(
OauthType.APPLE, socialLoginId, oauthRequest.name());
return new LoginResponse(loginProcessResult.accessToken());
LoginProcessResult result = loginManager.processAppleLogin(member);
return new LoginResponse(result.accessToken());
}

public String save(String idToken, String firstName, String lastName) {
log.info("idToken: {}, firstName: {}, lastName: {}", idToken, firstName, lastName);
String socialLoginId = appleOauthManager.getSocialLoginId(idToken);
Member member = new Member(lastName + firstName, new LoginDetail(OauthType.APPLE, socialLoginId));
Member savedMember = memberWriter.append(member);
return accessTokenProvider.provide(savedMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import mouda.backend.auth.Infrastructure.AppleOauthClient;
import mouda.backend.auth.util.TokenDecoder;

@Component
@RequiredArgsConstructor
public class AppleOauthManager {
private static final String SUB_CLAIM_KEY = "sub";

private final AppleOauthClient oauthClient;
private static final String SUB_CLAIM_KEY = "sub";

public String getSocialLoginId(String code) {
String idToken = oauthClient.getIdToken(code);
public String getSocialLoginId(String idToken) {
Map<String, String> payload = TokenDecoder.parseIdToken(idToken);
return payload.get(SUB_CLAIM_KEY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ public String getMemberName(String idToken) {
GoogleIdToken googleIdToken = getGoogleIdToken(idToken);
String familyName = (String)googleIdToken.getPayload().get("family_name");
String givenName = (String)googleIdToken.getPayload().get("given_name");
return getFullName(familyName, givenName);
}

private String getFullName(String familyName, String givenName) {
if (familyName == null) {
familyName = "";
}
if (givenName == null) {
givenName = "";
}
return familyName + givenName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ private LoginProcessResult processKakaoLogin(OauthType oauthType, String socialL
return new LoginProcessResult(newMember.getId(), accessTokenProvider.provide(newMember));
}

public LoginProcessResult processAppleLogin(Member member) {
return new LoginProcessResult(member.getId(), accessTokenProvider.provide(member));
}

public String updateOauth(long memberId, OauthType oauthType, String socialLoginId) {
Member member = memberFinder.findBySocialId(socialLoginId);
memberWriter.updateLoginDetail(memberId, oauthType, socialLoginId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mouda.backend.auth.presentation.controller;

import java.io.IOException;
import java.util.Map;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mouda.backend.auth.business.AppleAuthService;
import mouda.backend.auth.util.TokenDecoder;

@RestController
@Slf4j
@RequiredArgsConstructor
public class AppleAuthController {

private final AppleAuthService appleAuthService;
private final ObjectMapper objectMapper;

@PostMapping("/v1/oauth/apple")
public ResponseEntity<Void> test(
@RequestParam("code") String code,
@RequestParam("id_token") String id_token,
@RequestParam("user") String user
) throws IOException {
AppleUserInfoRequest request = objectMapper.readValue(user, AppleUserInfoRequest.class);

String firstName = request.name().firstName();
String lastName = request.name().lastName();
Map<String, String> stringStringMap = TokenDecoder.parseIdToken(id_token);
for (String s : stringStringMap.keySet()) {
log.info("{} : {}", s, stringStringMap.get(s));
}
log.error("firstName : {}, lastNAme: {}", firstName, lastName);
String accessToken = appleAuthService.save(id_token, firstName, lastName);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Location", "https://dev.mouda.site/oauth/apple?token=" + accessToken);
return new ResponseEntity<>(httpHeaders, HttpStatus.FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mouda.backend.auth.presentation.controller;

public record AppleUserInfoRequest(
AppleUserNameRequest name,
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mouda.backend.auth.presentation.controller;

public record AppleUserNameRequest(
String firstName,
String lastName
) {
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package mouda.backend.auth.presentation.request;

import jakarta.validation.constraints.NotNull;

public record AppleOauthRequest(
Long memberId,

@NotNull
String code,

@NotNull
String name
String nonce
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081", "https://dev.mouda.site", "https://mouda.site")
.allowedOrigins("http://localhost:8081", "https://dev.mouda.site", "https://mouda.site",
"https://appleid.apple.com")
.allowedMethods("GET", "POST", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("Authorization", "Content-Type")
.allowCredentials(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationCheckInterceptor)
.addPathPatterns("/v1/**")
.excludePathPatterns("/v1/auth/kakao/oauth", "/v1/auth/login", "/health", "/v1/auth/google/oauth",
"/v1/auth/apple/oauth");
"/v1/auth/apple/oauth", "/v1/oauth/apple");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
)
public class DarakbangMember {

private static final int MAX_LENGTH = 10;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand Down Expand Up @@ -74,6 +75,9 @@ private void validateNickname(String nickname) {
if (nickname == null || nickname.isBlank()) {
throw new DarakbangMemberException(HttpStatus.BAD_REQUEST, DarakbangMemberErrorMessage.NICKNAME_NOT_EXIST);
}
if (nickname.length() >= MAX_LENGTH) {
throw new DarakbangMemberException(HttpStatus.BAD_REQUEST, DarakbangMemberErrorMessage.INVALID_LENGTH);
}
}

public boolean isNotManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
public enum DarakbangMemberErrorMessage {

NICKNAME_NOT_EXIST("닉네임이 존재하지 않습니다."),
INVALID_LENGTH("닉네임은 9글자 이하로만 가능합니다."),
NICKNAME_ALREADY_EXIST("이미 존재하는 닉네임입니다."),
MEMBER_ALREADY_EXIST("이미 가입한 멤버입니다."),
MEMBER_NOT_EXIST("존재하지 않는 다락방 멤버입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class LoginDetail {

private String socialLoginId;

private String nonce;

protected LoginDetail() {
}

Expand All @@ -24,6 +26,12 @@ public LoginDetail(OauthType oauthType, String socialLoginId) {
this.socialLoginId = socialLoginId;
}

public LoginDetail(OauthType oauthType, String socialLoginId, String nonce) {
this.oauthType = oauthType;
this.socialLoginId = socialLoginId;
this.nonce = nonce;
}

@Override
public boolean equals(Object o) {
if (this == o)
Expand Down
5 changes: 0 additions & 5 deletions backend/src/main/java/mouda/backend/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
@NoArgsConstructor
public class Member {

private static final int NAME_MAX_LENGTH = 10;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand All @@ -42,9 +40,6 @@ private void validateName(String name) {
if (name.isBlank()) {
throw new MoimException(HttpStatus.BAD_REQUEST, MoimErrorMessage.MEMBER_NAME_NOT_EXISTS);
}
if (name.length() >= NAME_MAX_LENGTH) {
throw new MoimException(HttpStatus.BAD_REQUEST, MoimErrorMessage.MEMBER_NAME_TOO_LONG);
}
}

public String getSocialLoginId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ public Member findByMemberId(long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new AuthException(HttpStatus.NOT_FOUND, AuthErrorMessage.MEMBER_NOT_FOUND));
}

public Member findByNonce(String nonce) {
return memberRepository.findByLoginDetail_Nonce(nonce)
.orElseThrow(() -> new AuthException(HttpStatus.NOT_FOUND, AuthErrorMessage.MEMBER_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByLoginDetail_SocialLoginId(String socialLoginId);

Optional<Member> findByLoginDetail_Nonce(String nonce);

@Query("""
UPDATE Member m
SET m.loginDetail.oauthType = :oauthType, m.loginDetail.socialLoginId = :socialLoginId
Expand Down
2 changes: 1 addition & 1 deletion backend/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ oauth:
client-secret: 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
redirect-uri: https://dev.mouda.site/oauth/google
apple:
redirect-uri: https://dev.mouda.site/oauth/apple
redirect-uri: https://api.dev.mouda.site/oauth/apple

aws:
region:
Expand Down
2 changes: 1 addition & 1 deletion backend/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ oauth:
client-secret: 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
redirect-uri: http://localhost:8081/oauth/google
apple:
redirect-uri: https://dev.mouda.site/oauth/apple
redirect-uri: https://api.dev.mouda.site/oauth/apple

aws:
region:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package mouda.backend.auth.business;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

import java.util.Optional;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -15,10 +12,6 @@
import mouda.backend.auth.implement.AppleOauthManager;
import mouda.backend.auth.presentation.request.AppleOauthRequest;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.common.fixture.MemberFixture;
import mouda.backend.member.domain.LoginDetail;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
import mouda.backend.member.implement.MemberFinder;
import mouda.backend.member.infrastructure.MemberRepository;

Expand All @@ -42,13 +35,14 @@ class AppleAuthServiceTest {
@Disabled("실제 Resource Server에게 요청을 보내는 테스트이다. 프론트 서버를 켜서 코드르 발급 받아 필요할 때만 테스트한다.")
void oauthLogin() {
String code = "";
AppleOauthRequest oauthRequest = new AppleOauthRequest(1L, code, "anna");
AppleOauthRequest oauthRequest = new AppleOauthRequest(1L, "nonce");

LoginResponse loginResponse = appleAuthService.oauthLogin(oauthRequest);

assertThat(loginResponse).isNotNull();
}

/*
@DisplayName("사용자 전환을 시도하면 새로운 회원을 추가하지 않고 기존 회원의 정보를 수정한다.")
@Test
void oauthLoginConvertingMember() {
Expand All @@ -58,13 +52,13 @@ void oauthLoginConvertingMember() {
when(memberFinder.findBySocialId(anyString())).thenReturn(anna);
Long kakaoMemberId = anna.getId();
LoginResponse loginResponse = appleAuthService.oauthLogin(
new AppleOauthRequest(kakaoMemberId, "code", anna.getName()));
new AppleOauthRequest(kakaoMemberId, "nonce"));
assertThat(loginResponse).isNotNull();
Optional<Member> memberOptional = memberRepository.findById(kakaoMemberId);
assertThat(memberOptional.isPresent()).isTrue();
LoginDetail expected = new LoginDetail(OauthType.APPLE, appleSocialLoginId);
assertThat(memberOptional.get().getLoginDetail()).isEqualTo(expected);
}
}*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package mouda.backend.darakbangmember.domain;

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import mouda.backend.common.fixture.DarakbangSetUp;
import mouda.backend.darakbangmember.exception.DarakbangMemberException;

class DarakbangMemberTest extends DarakbangSetUp {

@DisplayName("닉네임은 10글자 미만 이여야 한다.")
@Test
void invalidNickName() {
assertThatThrownBy(() -> new DarakbangMember(
darakbang, 10L,
"hogeehogeehgoee", " ",
" ", DarakBangMemberRole.MEMBER))
.isInstanceOf(DarakbangMemberException.class)
.hasMessage("닉네임은 9글자 이하로만 가능합니다.");
}

}

0 comments on commit 06c3cc1

Please sign in to comment.