From 1188ec44b5c6cb9710bff27285cfd17253ba80c8 Mon Sep 17 00:00:00 2001 From: KTH1007 Date: Sun, 16 Nov 2025 23:38:44 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat/#38:=20OAuth=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B3=B5=ED=86=B5=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/request/OAuthCallbackRequest.java | 12 +++++++ .../api/dto/response/OAuthTokenResponse.java | 32 +++++++++++++++++++ .../global/oauth/config/OAuthConfig.java | 9 ++++++ .../global/oauth/config/OAuthProperties.java | 32 +++++++++++++++++++ src/main/resources/application-prod.yml | 6 ++++ src/main/resources/application.yml | 5 +++ 6 files changed, 96 insertions(+) create mode 100644 src/main/java/com/ganzi/backend/global/oauth/api/dto/request/OAuthCallbackRequest.java create mode 100644 src/main/java/com/ganzi/backend/global/oauth/api/dto/response/OAuthTokenResponse.java create mode 100644 src/main/java/com/ganzi/backend/global/oauth/config/OAuthConfig.java create mode 100644 src/main/java/com/ganzi/backend/global/oauth/config/OAuthProperties.java diff --git a/src/main/java/com/ganzi/backend/global/oauth/api/dto/request/OAuthCallbackRequest.java b/src/main/java/com/ganzi/backend/global/oauth/api/dto/request/OAuthCallbackRequest.java new file mode 100644 index 0000000..d866939 --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/api/dto/request/OAuthCallbackRequest.java @@ -0,0 +1,12 @@ +package com.ganzi.backend.global.oauth.api.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +@Schema(description = "OAuth 콜백 요청") +public record OAuthCallbackRequest( + @NotBlank(message = "Authorization code는 필수입니다") + @Schema(description = "OAuth 제공자로부터 받은 authorization code", example = "abc123xyz456") + String code +) { +} diff --git a/src/main/java/com/ganzi/backend/global/oauth/api/dto/response/OAuthTokenResponse.java b/src/main/java/com/ganzi/backend/global/oauth/api/dto/response/OAuthTokenResponse.java new file mode 100644 index 0000000..164e61f --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/api/dto/response/OAuthTokenResponse.java @@ -0,0 +1,32 @@ +package com.ganzi.backend.global.oauth.api.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "OAuth 토큰 응답") +public record OAuthTokenResponse( + @JsonProperty("access_token") + @Schema(description = "OAuth 액세스 토큰") + String accessToken, + + @JsonProperty("token_type") + @Schema(description = "토큰 타입", example = "Bearer") + String tokenType, + + @JsonProperty("refresh_token") + @Schema(description = "OAuth 리프레시 토큰") + String refreshToken, + + @JsonProperty("id_token") + @Schema(description = "OpenID Connect ID 토큰 (JWT)") + String idToken, + + @JsonProperty("expires_in") + @Schema(description = "액세스 토큰 만료 시간 (초)") + Integer expiresIn, + + @JsonProperty("scope") + @Schema(description = "허용된 권한 범위") + String scope +) { +} diff --git a/src/main/java/com/ganzi/backend/global/oauth/config/OAuthConfig.java b/src/main/java/com/ganzi/backend/global/oauth/config/OAuthConfig.java new file mode 100644 index 0000000..aa113f8 --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/config/OAuthConfig.java @@ -0,0 +1,9 @@ +package com.ganzi.backend.global.oauth.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(OAuthProperties.class) +public class OAuthConfig { +} diff --git a/src/main/java/com/ganzi/backend/global/oauth/config/OAuthProperties.java b/src/main/java/com/ganzi/backend/global/oauth/config/OAuthProperties.java new file mode 100644 index 0000000..97545b0 --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/config/OAuthProperties.java @@ -0,0 +1,32 @@ +package com.ganzi.backend.global.oauth.config; + +import com.ganzi.backend.global.code.status.ErrorStatus; +import com.ganzi.backend.global.exception.GeneralException; +import java.util.Map; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@RequiredArgsConstructor +@ConfigurationProperties(prefix = "oauth") +public class OAuthProperties { + + private final Map providers; + + @Getter + @RequiredArgsConstructor + public static class ProviderConfig { + private final String clientId; + private final String clientSecret; + private final String tokenUri; + } + + public ProviderConfig getProvider(String providerName) { + ProviderConfig config = providers.get(providerName); + if (config == null) { + throw new GeneralException(ErrorStatus.UNSUPPORTED_OAUTH_PROVIDER); + } + return config; + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b413fcc..dd3317a 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -36,3 +36,9 @@ jwt: refresh: expiration: ${JWT_REFRESH_EXPIRATION:1209600000} header: RefreshToken + +oauth: + providers: + kakao: + client-id: ${KAKAO_CLIENT_ID} + client-secret: ${KAKAO_CLIENT_SECRET} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 823e0c6..b0a43f7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -44,3 +44,8 @@ upstage: ai-space: api-key: ${UPSTAGE_AI_API_KEY} endpoint-url: ${UPSTAGE_AI_SPACE_ENDPOINT_URL:https://api.upstage.ai/v1/chat/completions} + +oauth: + providers: + kakao: + token-uri: https://kauth.kakao.com/oauth/token From dc5012f2c223a280eba3a000c8daa5ed36d3b3d2 Mon Sep 17 00:00:00 2001 From: KTH1007 Date: Sun, 16 Nov 2025 23:39:11 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat/#38:=20OAuth=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=B0=8F=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/client/KakaoOAuthClient.java | 82 +++++++++++++++++++ .../global/oauth/client/OAuthClient.java | 10 +++ 2 files changed, 92 insertions(+) create mode 100644 src/main/java/com/ganzi/backend/global/oauth/client/KakaoOAuthClient.java create mode 100644 src/main/java/com/ganzi/backend/global/oauth/client/OAuthClient.java diff --git a/src/main/java/com/ganzi/backend/global/oauth/client/KakaoOAuthClient.java b/src/main/java/com/ganzi/backend/global/oauth/client/KakaoOAuthClient.java new file mode 100644 index 0000000..8bab42f --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/client/KakaoOAuthClient.java @@ -0,0 +1,82 @@ +package com.ganzi.backend.global.oauth.client; + +import com.ganzi.backend.global.code.status.ErrorStatus; +import com.ganzi.backend.global.exception.GeneralException; +import com.ganzi.backend.global.oauth.api.dto.response.OAuthTokenResponse; +import com.ganzi.backend.global.oauth.config.OAuthProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Component +@RequiredArgsConstructor +public class KakaoOAuthClient implements OAuthClient { + + private static final String PROVIDER_NAME = "kakao"; + private static final String GRANT_TYPE = "authorization_code"; + + private final OAuthProperties oAuthProperties; + private final RestTemplate restTemplate = new RestTemplate(); + + @Override + public OAuthTokenResponse exchangeCodeForToken(String code) { + OAuthProperties.ProviderConfig config = oAuthProperties.getProvider(PROVIDER_NAME); + + try { + HttpHeaders headers = createHeaders(); + MultiValueMap body = createTokenRequestBody(code, config); + HttpEntity> request = new HttpEntity<>(body, headers); + + ResponseEntity response = restTemplate.exchange( + config.getTokenUri(), + HttpMethod.POST, + request, + OAuthTokenResponse.class + ); + + if (response.getBody() == null) { + throw new GeneralException(ErrorStatus.OAUTH_TOKEN_EXCHANGE_FAILED); + } + + log.info("카카오 토큰 교환 성공"); + return response.getBody(); + + } catch (RestClientException e) { + log.error("카카오 토큰 교환 실패: {}", e.getMessage(), e); + throw new GeneralException(ErrorStatus.OAUTH_TOKEN_EXCHANGE_FAILED); + } + } + + @Override + public String getProviderName() { + return PROVIDER_NAME; + } + + private HttpHeaders createHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + private MultiValueMap createTokenRequestBody( + String code, + OAuthProperties.ProviderConfig config + ) { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("grant_type", GRANT_TYPE); + body.add("client_id", config.getClientId()); + body.add("client_secret", config.getClientSecret()); + body.add("code", code); + return body; + } +} diff --git a/src/main/java/com/ganzi/backend/global/oauth/client/OAuthClient.java b/src/main/java/com/ganzi/backend/global/oauth/client/OAuthClient.java new file mode 100644 index 0000000..c6e43d3 --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/client/OAuthClient.java @@ -0,0 +1,10 @@ +package com.ganzi.backend.global.oauth.client; + +import com.ganzi.backend.global.oauth.api.dto.response.OAuthTokenResponse; + +public interface OAuthClient { + + OAuthTokenResponse exchangeCodeForToken(String code); + + String getProviderName(); +} From 2ca6f3024412195153321b9521690b1702a1bc7c Mon Sep 17 00:00:00 2001 From: KTH1007 Date: Sun, 16 Nov 2025 23:39:54 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat/#38:=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/api/AuthController.java | 15 ++++- .../oauth/api/doc/AuthControllerDoc.java | 16 +++-- .../global/oauth/application/AuthService.java | 13 ---- .../oauth/application/OAuthService.java | 66 +++++++++++++++++++ 4 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/ganzi/backend/global/oauth/application/OAuthService.java diff --git a/src/main/java/com/ganzi/backend/global/oauth/api/AuthController.java b/src/main/java/com/ganzi/backend/global/oauth/api/AuthController.java index 5dbb57f..a5d5b0f 100644 --- a/src/main/java/com/ganzi/backend/global/oauth/api/AuthController.java +++ b/src/main/java/com/ganzi/backend/global/oauth/api/AuthController.java @@ -2,16 +2,21 @@ import com.ganzi.backend.global.code.dto.ApiResponse; import com.ganzi.backend.global.oauth.api.doc.AuthControllerDoc; +import com.ganzi.backend.global.oauth.api.dto.request.OAuthCallbackRequest; import com.ganzi.backend.global.oauth.api.dto.response.LoginResponse; import com.ganzi.backend.global.oauth.api.dto.response.UserInfoResponse; import com.ganzi.backend.global.oauth.application.AuthService; +import com.ganzi.backend.global.oauth.application.OAuthService; import com.ganzi.backend.global.security.userdetails.CustomUserDetails; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -24,12 +29,16 @@ public class AuthController implements AuthControllerDoc { private static final String BEARER_PREFIX = "Bearer "; private static final int BEARER_PREFIX_LENGTH = 7; + private final OAuthService oAuthService; private final AuthService authService; @Override - @PostMapping("/login") - public ResponseEntity> login(@RequestHeader("id_token") String idToken) { - LoginResponse response = authService.login(idToken); + @PostMapping("/{provider}/callback") + public ResponseEntity> oauthCallback( + @PathVariable String provider, + @Valid @RequestBody OAuthCallbackRequest request + ) { + LoginResponse response = oAuthService.loginWithCode(provider, request.code()); return ResponseEntity.ok(ApiResponse.onSuccess(response)); } diff --git a/src/main/java/com/ganzi/backend/global/oauth/api/doc/AuthControllerDoc.java b/src/main/java/com/ganzi/backend/global/oauth/api/doc/AuthControllerDoc.java index ca4ce92..1f6417d 100644 --- a/src/main/java/com/ganzi/backend/global/oauth/api/doc/AuthControllerDoc.java +++ b/src/main/java/com/ganzi/backend/global/oauth/api/doc/AuthControllerDoc.java @@ -1,26 +1,32 @@ package com.ganzi.backend.global.oauth.api.doc; import com.ganzi.backend.global.code.dto.ApiResponse; +import com.ganzi.backend.global.oauth.api.dto.request.OAuthCallbackRequest; import com.ganzi.backend.global.oauth.api.dto.response.LoginResponse; import com.ganzi.backend.global.oauth.api.dto.response.UserInfoResponse; import com.ganzi.backend.global.security.userdetails.CustomUserDetails; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; @Tag(name = "로그인", description = "로그인 API") public interface AuthControllerDoc { @Operation( - summary = "카카오 소셜 로그인", - description = "카카오 ID Token으로 로그인하여 JWT 토큰을 발급받습니다" + summary = "OAuth 소셜 로그인 (Authorization Code 방식)", + description = "프론트엔드에서 받은 OAuth authorization code를 백엔드에서 토큰으로 교환하여 JWT를 발급합니다." ) - ResponseEntity> login( - @Parameter(description = "카카오 ID Token", required = true) - @RequestHeader("id_token") String idToken + ResponseEntity> oauthCallback( + @Parameter(description = "OAuth 제공자 (kakao, google, apple)", required = true, example = "kakao") + @PathVariable String provider, + + @Valid @RequestBody OAuthCallbackRequest request ); @Operation( diff --git a/src/main/java/com/ganzi/backend/global/oauth/application/AuthService.java b/src/main/java/com/ganzi/backend/global/oauth/application/AuthService.java index a63d35e..1980a34 100644 --- a/src/main/java/com/ganzi/backend/global/oauth/application/AuthService.java +++ b/src/main/java/com/ganzi/backend/global/oauth/application/AuthService.java @@ -23,19 +23,6 @@ public class AuthService { private final JwtService jwtService; private final UserRepository userRepository; - @Transactional - public LoginResponse login(String idToken) { - CustomUserDetails userDetails = idTokenService.loadUserByIdToken(idToken); - User user = userDetails.getUser(); - - String accessToken = jwtService.createAccessToken(user.getEmail(), user.getId()); - String refreshToken = jwtService.createRefreshToken(); - - jwtService.updateRefreshToken(user.getEmail(), refreshToken); - - return new LoginResponse(accessToken, refreshToken, user.getId(), user.getNickname()); - } - public UserInfoResponse getUserInfo(Long userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND)); diff --git a/src/main/java/com/ganzi/backend/global/oauth/application/OAuthService.java b/src/main/java/com/ganzi/backend/global/oauth/application/OAuthService.java new file mode 100644 index 0000000..394477d --- /dev/null +++ b/src/main/java/com/ganzi/backend/global/oauth/application/OAuthService.java @@ -0,0 +1,66 @@ +package com.ganzi.backend.global.oauth.application; + +import com.ganzi.backend.global.code.status.ErrorStatus; +import com.ganzi.backend.global.exception.GeneralException; +import com.ganzi.backend.global.oauth.api.dto.response.LoginResponse; +import com.ganzi.backend.global.oauth.api.dto.response.OAuthTokenResponse; +import com.ganzi.backend.global.oauth.client.OAuthClient; +import com.ganzi.backend.global.security.jwt.JwtService; +import com.ganzi.backend.global.security.userdetails.CustomUserDetails; +import com.ganzi.backend.user.User; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional(readOnly = true) +public class OAuthService { + + private final IdTokenService idTokenService; + private final JwtService jwtService; + private final Map oAuthClientMap; + + public OAuthService( + List oAuthClients, + IdTokenService idTokenService, + JwtService jwtService + ) { + this.oAuthClientMap = oAuthClients.stream() + .collect(Collectors.toMap( + OAuthClient::getProviderName, + Function.identity() + )); + this.idTokenService = idTokenService; + this.jwtService = jwtService; + } + + @Transactional + public LoginResponse loginWithCode(String provider, String code) { + OAuthClient oAuthClient = getOAuthClient(provider); + + OAuthTokenResponse tokenResponse = oAuthClient.exchangeCodeForToken(code); + + CustomUserDetails userDetails = idTokenService.loadUserByIdToken(tokenResponse.idToken()); + User user = userDetails.getUser(); + + String accessToken = jwtService.createAccessToken(user.getEmail(), user.getId()); + String refreshToken = jwtService.createRefreshToken(); + + jwtService.updateRefreshToken(user.getEmail(), refreshToken); + + return new LoginResponse(accessToken, refreshToken, user.getId(), user.getNickname()); + } + + private OAuthClient getOAuthClient(String provider) { + OAuthClient client = oAuthClientMap.get(provider.toLowerCase()); + if (client == null) { + throw new GeneralException(ErrorStatus.UNSUPPORTED_OAUTH_PROVIDER); + } + return client; + } +} From 1a4067cfbbc921f8a37a6b51ed524c94c53b8e77 Mon Sep 17 00:00:00 2001 From: KTH1007 Date: Sun, 16 Nov 2025 23:40:30 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat/#38:=20OAuth=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=B3=B4=EC=95=88=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ganzi/backend/global/code/status/ErrorStatus.java | 2 ++ .../java/com/ganzi/backend/global/config/SecurityConfig.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ganzi/backend/global/code/status/ErrorStatus.java b/src/main/java/com/ganzi/backend/global/code/status/ErrorStatus.java index a472508..c9f6413 100644 --- a/src/main/java/com/ganzi/backend/global/code/status/ErrorStatus.java +++ b/src/main/java/com/ganzi/backend/global/code/status/ErrorStatus.java @@ -17,6 +17,7 @@ public enum ErrorStatus implements BaseErrorCode { FILE_DOWNLOAD_FAILED(HttpStatus.BAD_REQUEST, "FILE400", "이미지 다운로드에 실패했습니다."), FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "FILE400", "파일 크기가 10MB를 초과했습니다."), INVALID_FILE_TYPE(HttpStatus.BAD_REQUEST, "FILE400", "지원하지 않는 파일 형식입니다. (JPG, PNG, WEBP만 지원)"), + UNSUPPORTED_OAUTH_PROVIDER(HttpStatus.BAD_REQUEST, "OAUTH400", "지원하지 않는 OAuth 제공자입니다."), // 401 Unauthorized UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "AUTH401", "인증이 필요합니다."), @@ -49,6 +50,7 @@ public enum ErrorStatus implements BaseErrorCode { S3_DELETE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "S3500", "파일 삭제 중 오류가 발생했습니다."), DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "DB500", "데이터베이스 처리 중 오류가 발생했습니다."), EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "API500", "외부 API 호출 중 오류가 발생했습니다."), + OAUTH_TOKEN_EXCHANGE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH500", "OAuth 토큰 교환에 실패했습니다."), // 5001,5002 // RAG 관련 오류 코드 diff --git a/src/main/java/com/ganzi/backend/global/config/SecurityConfig.java b/src/main/java/com/ganzi/backend/global/config/SecurityConfig.java index 7fc13de..82237d2 100644 --- a/src/main/java/com/ganzi/backend/global/config/SecurityConfig.java +++ b/src/main/java/com/ganzi/backend/global/config/SecurityConfig.java @@ -38,7 +38,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests(auth -> auth .requestMatchers( "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", - "/api/auth/login", "/api/auth/reissue" + "/api/auth/*/callback", "/api/auth/reissue" ).permitAll() .anyRequest().authenticated() ) From 06cc65babb6d869ac052e64ad4edf66d8b9ce038 Mon Sep 17 00:00:00 2001 From: KTH1007 Date: Sun, 16 Nov 2025 23:40:48 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat/#38:=20=EB=B0=B0=ED=8F=AC=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c673b37..8ecd9d8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -74,6 +74,8 @@ jobs: ANIMAL_API_SERVICE_KEY: ${{ secrets.ANIMAL_API_SERVICE_KEY }} UPSTAGE_API_KEY: ${{ secrets.UPSTAGE_API_KEY }} UPSTAGE_AI_API_KEY: ${{ secrets.UPSTAGE_AI_API_KEY }} + KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }} + KAKAO_CLIENT_SECRET: ${{ secrets.KAKAO_CLIENT_SECRET }} with: host: ${{ secrets.EC2_HOST }} username: ${{ secrets.EC2_USERNAME }} @@ -84,7 +86,8 @@ jobs: S3_REGION,S3_BUCKET_NAME, AWS_ACCESS_KEY,AWS_SECRET_ACCESS_KEY, JWT_SECRET_KEY,ANIMAL_API_SERVICE_KEY, - UPSTAGE_API_KEY, UPSTAGE_AI_API_KEY + UPSTAGE_API_KEY, UPSTAGE_AI_API_KEY, + KAKAO_CLIENT_ID,KAKAO_CLIENT_SECRET script: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $(echo $IMAGE_URI | cut -d'/' -f1) @@ -109,4 +112,6 @@ jobs: -e ANIMAL_API_SERVICE_KEY=$ANIMAL_API_SERVICE_KEY \ -e UPSTAGE_API_KEY=$UPSTAGE_API_KEY \ -e UPSTAGE_AI_API_KEY=$UPSTAGE_AI_API_KEY \ + -e KAKAO_CLIENT_ID=$KAKAO_CLIENT_ID \ + -e KAKAO_CLIENT_SECRET=$KAKAO_CLIENT_SECRET \ $IMAGE_URI:$IMAGE_TAG