나와바리 Rest API OAuth2 로그인

Sungmin·2023년 9월 7일
0

1. 사용자가 로그인 버튼을 클릭

2. 프론트엔드는 사용자를 Kakao 로그인 페이지로 리다이렉션합니다.

3. 사용자가 Kakao 로그인 페이지에서 로그인을 수행하고 권한을 부여

4. 사용자가 로그인을 진행하고, 정보 사용에 동의한다.

5. 카카오 서버가 프론트에서 설정된 Redirect URI로 인증 결과를 전달하고 이 때 인가코드가 발급된다.

6. 백엔드 서버에서는 프론트엔드로부터 전달받은 인가코드를 사용하여 Kakao로부터 액세스 토큰 및 사용자 정보를 가져온다.

7. 사용자 정보를 활용해 JWT토큰을 생성하고 클라이언트에게 응답.


1. 인가코드를 받아와서 로그인 처리 후 응답하는 엔드포인트 생성

@PostMapping("/api/auth/kakao")
    public ResponseEntity<AddInfoResponse> loginKakao(@RequestBody KakaoLoginParams params, HttpServletResponse response) {
        AuthTokens authTokens = oAuthLoginService.login(params);
        String accessToken = authTokens.getAccessToken();
        String refreshToken = authTokens.getRefreshToken();
        Long member_id = authTokens.getMember_id();
        response.setHeader(jwtService.getAccessHeader(), "Bearer " + accessToken);
        response.setHeader(jwtService.getRefreshHeader(), "Bearer " + refreshToken);
        response.setHeader("member_id", member_id.toString());
        return ResponseEntity.ok(new AddInfoResponse("Bearer", jwtService.getAccessTokenExpirationPeriod()));
    }

2. 인가코드로 액세스토큰을 발급하고, 회원정보를 받아와 회원이 존재하면 액세스토큰과 리프레시토큰을 반환. 이 때 리프레시 토큰은 DB에 저장.

public AuthTokens login(OAuthLoginParams params) {
        OAuthInfoResponse oAuthInfoResponse = requestOAuthInfoService.request(params);
        Long memberId = findOrCreateMember(oAuthInfoResponse);
        Member member = memberRepository.findOne(memberId);

        String accessToken = jwtService.createAccessToken(member.getEmail());
        String refreshToken = jwtService.createRefreshToken();

        memberRepository.saveRefreshToken(member.getEmail(), refreshToken);

        return AuthTokens.of(memberId, accessToken, refreshToken);
    }

회원이 존재하면 Member 엔티티를 반환하고 존재하지 않을 경우 회원가입 처리한다. 단 이메일 제공에 동의하지 않을경우 UUID로 생성한 메일을 저장한다.


private Long findOrCreateMember(OAuthInfoResponse oAuthInfoResponse) {
        return memberRepository.findByEmail(oAuthInfoResponse.getEmail())
                .map(Member::getId)
                .orElseGet(() -> newMember(oAuthInfoResponse));
    }

    private Long newMember(OAuthInfoResponse oAuthInfoResponse) {

        String email = oAuthInfoResponse.getEmail();
        if (email == null) {
            email = UUID.randomUUID() + "@nawabari.com";
        }

        Member member = Member.builder()
                .kakao_id(oAuthInfoResponse.getKakao_id())
                .age(oAuthInfoResponse.getAgeRange())
                .gender(oAuthInfoResponse.getGender())
                .socialType(SocialType.KAKAO)
                .email(email)
                .profile_nickname(oAuthInfoResponse.getNickname())
                .profile_image(oAuthInfoResponse.getProfileImage())
                .role(Role.GUEST)
                .build();

        memberRepository.save(member);
        return member.getId();
    }

#액세스토큰을 발급받아 회원의 정보를 가져오는 코드는 생략하였다.#




이후 프론트엔드에서 헤더와 바디에 담아 응답된 정보를 받지 못하고 앱으로 리다이렉트 하는 과정을 수행하지 못하여 HTML형식의 앱으로 리다이렉트하는 버튼과 함께 데이터를 반환하였다.

@GetMapping("/login/oauth2/code/kakao")
    public String loginKakao(@RequestParam("code") String code) {
        KakaoLoginParams params = new KakaoLoginParams(code);

        AuthTokens authTokens = oAuthLoginService.login(params);

        String accessToken = authTokens.getAccessToken();
        String refreshToken = authTokens.getRefreshToken();
        Long memberId = authTokens.getMember_id();

        return  "<!DOCTYPE html>" +
                "<html lang='en'>" +
                "<head>" +
                "<meta charset='UTF-8'>" +
                "<meta name='viewport' content='width=device-width, initial-scale=1.0'>" +
                "<title>Redirect to App Test</title>" +
                "</head>" +
                "<body>" +
                "<button onclick='redirectToApp()'>앱으로 돌아가기</button>" +
                "<script>" +
                "function redirectToApp() {" +
                "window.location.href = 'callback-scheme://?access-token="+accessToken+"&refresh-token="+refreshToken+"&member-id="+memberId + "';" +
                "}" +
                "</script>" +
                "</body>" +
                "</html>";
    }
profile
Let's Coding

0개의 댓글