Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClient;
import org.tuna.zoopzoop.backend.domain.auth.service.KakaoUserInfoService;
import org.tuna.zoopzoop.backend.domain.member.entity.Member;
import org.tuna.zoopzoop.backend.domain.member.service.MemberService;
import org.tuna.zoopzoop.backend.global.config.jwt.JwtProperties;
Expand All @@ -23,6 +25,8 @@ public class ApiV1AuthController {
private final JwtUtil jwtUtil;
private final MemberService memberService;
private final JwtProperties jwtProperties;
private final KakaoUserInfoService kakaoUserInfoService;
private final WebClient webClient;

/**
* 사용자 로그아웃 API
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.tuna.zoopzoop.backend.domain.auth.global;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;

public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {

private final OAuth2AuthorizationRequestResolver defaultResolver;

public CustomOAuth2AuthorizationRequestResolver(ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
}

@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
return customize(defaultResolver.resolve(request), request);
}

@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
return customize(defaultResolver.resolve(request, clientRegistrationId), request);
}

private OAuth2AuthorizationRequest customize(OAuth2AuthorizationRequest req, HttpServletRequest request) {
if (req == null) return null;

String source = request.getParameter("source"); // 로그인 시작 시 전달된 source

OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.from(req);

if ("extension".equals(source)) {
// state에 source 정보를 안전하게 포함
builder.state("source:extension;" + req.getState());
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ public void onAuthenticationFailure(HttpServletRequest request,
// 프론트로 리다이렉트
// 필요하면 쿼리 파라미터로 에러 정보 전달

String source = request.getParameter("source");

if("extension".equals(source)){
String redirectUrl = redirect_domain + "/extension/callback "
+ "?success=false"
+ "&error=" + URLEncoder.encode(exception.getMessage(), "UTF-8");
response.sendRedirect(redirectUrl);
return;
}

String redirectUrl =
redirect_domain + "/auth/callback"
+ "?success=false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
Expand All @@ -22,8 +23,8 @@

@Component
@RequiredArgsConstructor
@Slf4j
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

private final JwtUtil jwtUtil;
private final JwtProperties jwtProperties;
private final MemberRepository memberRepository;
Expand Down Expand Up @@ -61,13 +62,30 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
String accessToken = jwtUtil.generateToken(member);
String refreshToken = jwtUtil.generateRefreshToken(member);

if ("server".equals(activeProfile)) {
String source = request.getParameter("source");
String state = request.getParameter("state");
log.info("[OAuth2SuccessHandler] Source: {}", source);
log.info("[OAuth2SuccessHandler] State: {}", state);
boolean isExtension = state != null && state.contains("source:extension");

// 확장 프로그램에서 로그인 했을 경우.
if(isExtension){
String redirectUrl = redirect_domain + "/extension/callback "
+ "?success=true"
+ "&accessToken=" + URLEncoder.encode(accessToken, "UTF-8")
+ "&refreshToken=" + URLEncoder.encode(refreshToken, "UTF-8");
response.sendRedirect(redirectUrl);
return;
}

if ("http://localhost:3000".equals(redirect_domain)) {
// server 환경일 때: URL 파라미터로 토큰 전달
String redirectUrl = redirect_domain + "/auth/callback"
+ "?success=true"
+ "&accessToken=" + URLEncoder.encode(accessToken, "UTF-8")
+ "&refreshToken=" + URLEncoder.encode(refreshToken, "UTF-8");
response.sendRedirect(redirectUrl);

} else {
ResponseCookie accessCookie = ResponseCookie.from("accessToken", accessToken)
.httpOnly(true)
Expand Down Expand Up @@ -95,11 +113,6 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
response.addHeader(HttpHeaders.SET_COOKIE, accessCookie.toString());
response.addHeader(HttpHeaders.SET_COOKIE, refreshCookie.toString());

String redirectUrl = redirect_domain + "/auth/callback"
+ "?success=true"
+ "&accessToken=" + URLEncoder.encode(accessToken, "UTF-8")
+ "&refreshToken=" + URLEncoder.encode(refreshToken, "UTF-8");

// 로그인 성공 후 리다이렉트.
// 배포 시에 프론트엔드와 조율이 필요한 부분일 듯 함.
response.sendRedirect(redirect_domain + "/auth/callback");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.tuna.zoopzoop.backend.domain.auth.global.CustomOAuth2AuthorizationRequestResolver;
import org.tuna.zoopzoop.backend.domain.auth.handler.OAuth2SuccessHandler;
import org.tuna.zoopzoop.backend.domain.auth.service.CustomOAuth2UserService;
import org.tuna.zoopzoop.backend.global.security.jwt.CustomAuthenticationEntryPoint;
Expand All @@ -18,6 +20,7 @@ public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomOAuth2UserService customOAuth2UserService;
private final OAuth2SuccessHandler oAuth2SuccessHandler;
private final ClientRegistrationRepository clientRegistrationRepository;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
Expand All @@ -44,6 +47,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(authorization -> authorization
.authorizationRequestResolver(
new CustomOAuth2AuthorizationRequestResolver(
clientRegistrationRepository,
"/oauth2/authorization"
)
))
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService)
)
Expand All @@ -58,7 +68,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.exceptionHandling(ex -> ex
.authenticationEntryPoint(customAuthenticationEntryPoint)
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);;
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}