
1) 프론트 단에서 리다이렉트로 kakao Access Token 발급
2) -> backend로 전송
3) 백엔드 단에서 토큰을 RequestBody 인자 형태로 전달받아,
토큰 인증을 거쳐 -> JWT 발급 -> 리턴
4) 인증 완료
이러면 엑세스 토큰과 발급받기 위해 전달하는 비밀 키가 클라이언트 단에 노출되는 문제가 생긴다.
민감한 정보는 안전하고, 클라이언트는 가볍게
1) 프론트 단에서 로그인 요청(GET) -> backend로 전송
2) 백엔드 단에서 요청을 받아 리다이렉트로 카카오 서버로부터 Access Token 발급
3) 발급받은 토큰을 가지고 사용자 인증 후, JWT 발급 -> 리턴
4) 인증 완료
기대 효과
① Access Token 보호
프론트엔드는 인증 코드(Authorization Code)만 전달하므로 Access Token이 클라이언트에 노출되지 않음
② HTTPS 사용
HTTPS를 통해 통신하면 중간자 공격(Man-In-The-Middle, MITM)도 방지됨
근데 이런 흐름으로 하면,
카카오 리다이렉트 URL에 Access Token을 실어 전달받으니,
사실 상 Access Token이 노출되는 건 매한가지였다.
이처럼 OAuth2.0에서 인증코드 없이 바로 Access Token을 발급받는
Implicit Grant방식은 보안 이슈로 권장되지 않기 때문에
현재 가장 널리 쓰이는 Authorization Code Grant방식을 사용하기로 함.
인증코드를 사용해 Access Token을 요청하는 방식은
카카오 디벨로퍼 공식 문서에도 나와있었다..

이제야 이해가 가는 흐름..!
무작정 따라하기보다 흐름을 이해하기 위해
혼자 설계하고 부딫히니 문제점을 깨달을 수 있었다.
뭐든 왜? 라는 게 해결되어야 하는 사람이라
그냥 따라하는 것에는 흥미가 없는데, 삽질을 거치고 나니
이 인증방식을 왜 쓰는지 알 수 있었음.
'이건 프론트가 해주면 안되나?'->'근데 이렇게하면 보안 위협이 없을까?'->'OAuth2.0 인증을 해야겠구나!'->'이래도 취약점이 생기네?'->'OAuth2.0에서 자주 쓰는 인증 방식이 코드 인증 방식이고 이게 Access Token 탈취로부터 근본적인 해결을 도와주는구나!'-> 공식문서 이해 완료
1) 프론트 카카오 로그인 요청(GET) → clientId + redirectUrl(콜백API) -> 카카오redirect page에서 사용자 동의 -> Authorization Code 발급
2) 인증 코드로 /kakao/callback 엔드포인트 리다이렉트
-> Access Token 요청 https://kauth.kakao.com/oauth/token
-> 사용자 이메일 요청 https://kapi.kakao.com/v2/user/me
3) 인증받은 이메일-> Users DB 조회(사용자 존재 여부 확인)
-> 없으면 신규 사용자 저장(회원가입), 있으면 바로 JWT 발급(로그인)
4) JWT 반환, 인증 완료
@GetMapping("/kakao/login")
public String redirectToKakao() {
try {
String clientId = env.getProperty("spring.security.oauth2.client.registration.kakao.client-id");
String redirectUri = env.getProperty("spring.security.oauth2.client.registration.kakao.redirect-uri");
if (clientId == null || redirectUri == null) {
throw new IllegalStateException("Kakao OAuth configuration is missing in the application.yml");
}
String kakaoAuthUrl = String.format(
"https://kauth.kakao.com/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code",
clientId, redirectUri);
return "redirect:" + kakaoAuthUrl;
} catch (Exception e) {
System.err.println("Error generating Kakao OAuth URL: " + e.getMessage());
return "error/redirect-error";
}
}
@GetMapping("/kakao/callback")
public ResponseEntity<String> handleKakaoCallback(@RequestParam String code) {
log.info("Request Parameters: {}", code);
try {
String jwtToken = kakaoService.authenticateWithKakao(code);
return ResponseEntity.ok(jwtToken);
} catch (Exception e) {
System.err.println("Error during Kakao authentication: " + e.getMessage());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication failed: " + e.getMessage());
}
}
JWT 생성 시에는
가장 널리 사용되는 알고리즘이자 보안 수준이 높은 대칭키 알고리즘 HS256을 사용했다.
RFC 7518 규격에 따라 최소 256비트 이상의 키를 사용해야 보안이 유지됨.
대칭키 특성 상 토큰 생성 시, 인증 필터 모두 같은 비밀키를 사용해야했기 때문에
yml파일로 따로 빼고 환경변수 설정함

적절한 인증 과정을 거쳐 JWT 발급이 완료되었다.