From bce822394bf861bd842ade37e7e67a1ec7b241d1 Mon Sep 17 00:00:00 2001 From: minij02 Date: Sun, 14 Jun 2026 00:53:02 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20OAuth=20=ED=86=A0=ED=81=B0=20=EA=B5=90?= =?UTF-8?q?=ED=99=98=20redirect=5Furi=20=EB=88=84=EB=9D=BD=20=EC=8B=9C=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20fallback=20(#515)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - body의 redirect_uri 가 없으면 환경변수의 *_CALLBACK_URL 로 fallback (긴급 호환) - 있으면 기존 화이트리스트 검증 후 사용 - 프론트 단계적 반영을 위한 임시 호환 경로 - Swagger 명세는 redirect_uri 를 필수로 유지 (공식 계약은 새 동작) --- src/auth/controllers/auth.controller.ts | 14 +++++++------- src/auth/services/auth.service.ts | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 31d3f41..b7a2bc1 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import AuthService, { assertAllowedRedirectUri } from "../services/auth.service"; +import AuthService, { resolveRedirectUri } from "../services/auth.service"; import { AppError } from "../../errors/AppError"; import { CompleteSignupDto } from "../dtos/complete-signup.dto"; import { validate } from "class-validator"; @@ -328,8 +328,8 @@ class AuthController { }); } - const validatedRedirectUri = assertAllowedRedirectUri(redirect_uri); - const result = await AuthService.exchangeKakaoToken(code, validatedRedirectUri); + const resolvedRedirectUri = resolveRedirectUri("KAKAO", redirect_uri); + const result = await AuthService.exchangeKakaoToken(code, resolvedRedirectUri); res.status(200).json({ message: "카카오 로그인이 완료되었습니다.", @@ -365,8 +365,8 @@ class AuthController { }); } - const validatedRedirectUri = assertAllowedRedirectUri(redirect_uri); - const result = await AuthService.exchangeGoogleToken(code, validatedRedirectUri); + const resolvedRedirectUri = resolveRedirectUri("GOOGLE", redirect_uri); + const result = await AuthService.exchangeGoogleToken(code, resolvedRedirectUri); res.status(200).json({ message: "구글 로그인이 완료되었습니다.", @@ -402,8 +402,8 @@ class AuthController { }); } - const validatedRedirectUri = assertAllowedRedirectUri(redirect_uri); - const result = await AuthService.exchangeNaverToken(code, validatedRedirectUri); + const resolvedRedirectUri = resolveRedirectUri("NAVER", redirect_uri); + const result = await AuthService.exchangeNaverToken(code, resolvedRedirectUri); res.status(200).json({ message: "네이버 로그인이 완료되었습니다.", diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index d168de2..6f3bda6 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -35,6 +35,23 @@ export const assertAllowedRedirectUri = (redirectUri: string | undefined): strin return redirectUri; }; +// 호환성 fallback: body의 redirect_uri가 없을 때 환경변수의 provider별 callback URL을 사용한다. +// 프론트 단계적 반영을 위한 임시 호환 경로. +export const resolveRedirectUri = ( + provider: "GOOGLE" | "KAKAO" | "NAVER", + bodyRedirectUri: string | undefined +): string => { + if (bodyRedirectUri) { + return assertAllowedRedirectUri(bodyRedirectUri); + } + const envKey = `${provider}_CALLBACK_URL` as const; + const fallback = process.env[envKey]; + if (!fallback) { + throw new AppError("redirect_uri가 필요합니다.", 400, "BadRequest"); + } + return fallback; +}; + class AuthService { async generateTokens(user: any): Promise { const accessToken = jwt.sign(