44
55import org .springframework .http .HttpHeaders ;
66import org .springframework .http .HttpStatus ;
7+ import org .springframework .http .ResponseCookie ;
78import org .springframework .http .ResponseEntity ;
89import org .springframework .security .core .annotation .AuthenticationPrincipal ;
10+ import org .springframework .util .StringUtils ;
911import org .springframework .web .bind .annotation .CookieValue ;
1012import org .springframework .web .bind .annotation .GetMapping ;
1113import org .springframework .web .bind .annotation .PathVariable ;
1214import org .springframework .web .bind .annotation .RequestParam ;
1315import org .springframework .web .bind .annotation .RestController ;
16+ import org .springframework .web .util .UriComponentsBuilder ;
1417
1518import jakarta .servlet .http .HttpServletRequest ;
1619import lombok .RequiredArgsConstructor ;
1720import lombok .extern .slf4j .Slf4j ;
1821import project .flipnote .auth .constants .OAuthConstants ;
1922import project .flipnote .auth .exception .AuthErrorCode ;
2023import project .flipnote .auth .model .AuthorizationRedirect ;
24+ import project .flipnote .auth .model .TokenPair ;
2125import project .flipnote .auth .service .OAuthService ;
2226import project .flipnote .common .config .ClientProperties ;
2327import project .flipnote .common .exception .BizException ;
2428import project .flipnote .common .security .dto .UserAuth ;
29+ import project .flipnote .common .security .jwt .JwtConstants ;
30+ import project .flipnote .common .security .jwt .JwtProperties ;
31+ import project .flipnote .common .util .CookieUtil ;
2532
2633@ Slf4j
2734@ RequiredArgsConstructor
@@ -30,14 +37,16 @@ public class OAuthController {
3037
3138 private final OAuthService oAuthService ;
3239 private final ClientProperties clientProperties ;
40+ private final JwtProperties jwtProperties ;
41+ private final CookieUtil cookieUtil ;
3342
3443 @ GetMapping ("/oauth2/authorization/{provider}" )
3544 public ResponseEntity <Void > redirectToProviderAuthorization (
3645 @ PathVariable ("provider" ) String provider ,
3746 HttpServletRequest request ,
3847 @ AuthenticationPrincipal UserAuth userAuth
3948 ) {
40- AuthorizationRedirect authRedirect = oAuthService .getAuthorizationUri (provider , request , userAuth . userId () );
49+ AuthorizationRedirect authRedirect = oAuthService .getAuthorizationUri (provider , request , userAuth );
4150
4251 return ResponseEntity .status (HttpStatus .FOUND )
4352 .header (HttpHeaders .SET_COOKIE , authRedirect .cookie ().toString ())
@@ -49,31 +58,103 @@ public ResponseEntity<Void> redirectToProviderAuthorization(
4958 public ResponseEntity <Void > handleCallback (
5059 @ PathVariable ("provider" ) String provider ,
5160 @ RequestParam ("code" ) String code ,
52- @ RequestParam ("state" ) String state ,
61+ @ RequestParam (name = "state" , required = false ) String state ,
5362 @ CookieValue (OAuthConstants .VERIFIER_COOKIE_NAME ) String codeVerifier ,
5463 HttpServletRequest request
5564 ) {
56- String redirectUri = clientProperties .buildUrl (ClientProperties .PathKey .SOCIAL_LINK_SUCCESS );
65+ boolean isSocialLinkRequest = StringUtils .hasText (state );
66+ if (isSocialLinkRequest ) {
67+ return handleSocialLink (provider , code , state , codeVerifier , request );
68+ }
69+ return handleSocialLogin (provider , code , codeVerifier , request );
70+ }
71+
72+ private ResponseEntity <Void > handleSocialLink (
73+ String provider ,
74+ String code ,
75+ String state ,
76+ String codeVerifier ,
77+ HttpServletRequest request
78+ ) {
79+ URI location ;
5780 try {
5881 oAuthService .linkSocialAccount (provider , code , state , codeVerifier , request );
59- } catch (BizException exception ) {
60- if (exception .getErrorCode () == AuthErrorCode .ALREADY_LINKED_SOCIAL_ACCOUNT ) {
61- redirectUri = clientProperties .buildUrl (ClientProperties .PathKey .SOCIAL_LINK_CONFLICT );
62- } else {
63- redirectUri = clientProperties .buildUrl (ClientProperties .PathKey .SOCIAL_LINK_FAILURE );
64- }
65- log .warn ("BizException handled: code={}, status={}, message={}" ,
66- exception .getErrorCode ().getCode (),
67- exception .getErrorCode ().getStatus (),
68- exception .getErrorCode ().getMessage ()
69- );
70- } catch (Exception exception ) {
71- redirectUri = clientProperties .buildUrl (ClientProperties .PathKey .SOCIAL_LINK_FAILURE );
72- log .error ("소셜 계정 연동 콜백 처리 중 예상치 못한 오류 발생. provider: {}, state: {}" , provider , state , exception );
82+ location = buildRedirectUri (ClientProperties .PathKey .SOCIAL_LINK_SUCCESS );
83+ } catch (BizException ex ) {
84+ location = resolveRedirectUrlForSocialLink (ex );
85+ logBizException (ex );
86+ } catch (Exception ex ) {
87+ location = buildRedirectUri (ClientProperties .PathKey .SOCIAL_LINK_FAILURE );
88+ log .error ("소셜 계정 연동 콜백 처리 중 예상치 못한 오류 발생. provider: {}, state: {}" , provider , state , ex );
7389 }
7490
75- return ResponseEntity .status (HttpStatus .FOUND )
76- .location (URI .create (redirectUri ))
77- .build ();
91+ return buildRedirectResponse (location , null );
92+ }
93+
94+ private ResponseEntity <Void > handleSocialLogin (
95+ String provider ,
96+ String code ,
97+ String codeVerifier ,
98+ HttpServletRequest request
99+ ) {
100+ URI location ;
101+ ResponseCookie refreshTokenCookie = null ;
102+ try {
103+ TokenPair tokenPair = oAuthService .socialLogin (provider , code , codeVerifier , request );
104+ location = buildLoginSuccessRedirectUri (tokenPair .accessToken ());
105+ refreshTokenCookie = createRefreshTokenCookie (tokenPair .refreshToken ());
106+ } catch (BizException ex ) {
107+ location = buildRedirectUri (ClientProperties .PathKey .SOCIAL_LOGIN_FAILURE );
108+ logBizException (ex );
109+ } catch (Exception ex ) {
110+ location = buildRedirectUri (ClientProperties .PathKey .SOCIAL_LOGIN_FAILURE );
111+ log .error ("소셜 계정 로그인 콜백 처리 중 예상치 못한 오류 발생. provider: {}" , provider , ex );
112+ }
113+
114+ return buildRedirectResponse (location , refreshTokenCookie );
115+ }
116+
117+ private void logBizException (BizException ex ) {
118+ log .warn ("BizException handled: code={}, status={}, message={}" ,
119+ ex .getErrorCode ().getCode (),
120+ ex .getErrorCode ().getStatus (),
121+ ex .getErrorCode ().getMessage ()
122+ );
123+ }
124+
125+ private ResponseCookie createRefreshTokenCookie (String token ) {
126+ long expirationSeconds = jwtProperties .getRefreshTokenExpiration ().toSeconds ();
127+ return cookieUtil .createCookie (
128+ JwtConstants .REFRESH_TOKEN ,
129+ token ,
130+ Math .toIntExact (expirationSeconds )
131+ );
132+ }
133+
134+ private URI resolveRedirectUrlForSocialLink (BizException exception ) {
135+ if (exception .getErrorCode () == AuthErrorCode .ALREADY_LINKED_SOCIAL_ACCOUNT ) {
136+ return buildRedirectUri (ClientProperties .PathKey .SOCIAL_LINK_CONFLICT );
137+ }
138+ return buildRedirectUri (ClientProperties .PathKey .SOCIAL_LINK_FAILURE );
139+ }
140+
141+ private URI buildRedirectUri (ClientProperties .PathKey pathKey ) {
142+ return URI .create (clientProperties .buildUrl (pathKey ));
143+ }
144+
145+ private URI buildLoginSuccessRedirectUri (String accessToken ) {
146+ return UriComponentsBuilder
147+ .fromUriString (clientProperties .buildUrl (ClientProperties .PathKey .SOCIAL_LOGIN_SUCCESS ))
148+ .queryParam ("accessToken" , accessToken )
149+ .build (true )
150+ .toUri ();
151+ }
152+
153+ private ResponseEntity <Void > buildRedirectResponse (URI location , ResponseCookie cookie ) {
154+ ResponseEntity .BodyBuilder builder = ResponseEntity .status (HttpStatus .FOUND ).location (location );
155+ if (cookie != null ) {
156+ builder .header (HttpHeaders .SET_COOKIE , cookie .toString ());
157+ }
158+ return builder .build ();
78159 }
79160}
0 commit comments