diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/controller/ApiV1AuthController.java b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/controller/ApiV1AuthController.java index 94f534fb..60d187ba 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/controller/ApiV1AuthController.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/controller/ApiV1AuthController.java @@ -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; @@ -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 diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/global/CustomOAuth2AuthorizationRequestResolver.java b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/global/CustomOAuth2AuthorizationRequestResolver.java new file mode 100644 index 00000000..04429882 --- /dev/null +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/global/CustomOAuth2AuthorizationRequestResolver.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2FailureHandler.java b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2FailureHandler.java index 58197968..cb8f6dc5 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2FailureHandler.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2FailureHandler.java @@ -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" diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2SuccessHandler.java b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2SuccessHandler.java index ec8d6048..f54de5b6 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2SuccessHandler.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2SuccessHandler.java @@ -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; @@ -22,8 +23,8 @@ @Component @RequiredArgsConstructor +@Slf4j public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - private final JwtUtil jwtUtil; private final JwtProperties jwtProperties; private final MemberRepository memberRepository; @@ -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) @@ -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"); diff --git a/src/main/java/org/tuna/zoopzoop/backend/global/security/SecurityConfig.java b/src/main/java/org/tuna/zoopzoop/backend/global/security/SecurityConfig.java index 93eb25bc..dd94782a 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/global/security/SecurityConfig.java +++ b/src/main/java/org/tuna/zoopzoop/backend/global/security/SecurityConfig.java @@ -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; @@ -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 { @@ -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) ) @@ -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(); } } \ No newline at end of file