diff --git a/.env b/.env index 6b26492a..6749a85b 100644 --- a/.env +++ b/.env @@ -75,3 +75,7 @@ VWORLD_SESSION_COOKIE= # True면 대시보드 삭제 버튼이 DB 행과 storage/.../{프로젝트ID}/ 폴더를 실제로 지운다. # 배포 전 반드시 False로 되돌릴 것. PROJECT_DELETE_HARD_ENABLED=True + +# 개발 단계에서 로그인 이메일 인증(OTP)을 건너뛴다 (2026-09-13 사용자 지시). +# ENVIRONMENT=development 일 때만 듣는다. 배포 전 반드시 False 로 되돌릴 것. +AUTH_OTP_DISABLED=True diff --git a/A06_Login/A06_Login_Router.py b/A06_Login/A06_Login_Router.py index b653ead5..e60f62f4 100644 --- a/A06_Login/A06_Login_Router.py +++ b/A06_Login/A06_Login_Router.py @@ -1,5 +1,6 @@ """로그인, 재인증, 세션 및 비밀번호 API.""" +import logging from datetime import datetime, timedelta from fastapi import APIRouter, Depends, HTTPException, Request @@ -34,10 +35,12 @@ from common_util.common_util_auth_repository import ( ) from common_util.common_util_email import send_email_background from common_util.common_util_email_templates import otp_email, security_alert_email -from config.config_system import ADMIN_EMAIL, EMAIL_REVERIFY_DAYS +from config.config_system import ADMIN_EMAIL, AUTH_OTP_DISABLED, EMAIL_REVERIFY_DAYS from .A06_Login_Schema import LoginRequest, OtpVerifyRequest, PasswordChangeRequest +logger = logging.getLogger(__name__) + router = APIRouter(prefix="/api/auth", tags=["Authentication"]) @@ -85,11 +88,20 @@ async def request_login(payload: LoginRequest, request: Request): ) new_browser = not trusted_device if periodic_reverify or new_browser: - await _send_otp(user, "LOGIN", "로그인") - return { - "status": "otp_required", - "reason": "PERIODIC" if periodic_reverify else "NEW_BROWSER", - } + # 개발 단계에서는 메일 인증을 건너뛴다 (2026-09-13 사용자 지시). + # 이 갈래는 ENVIRONMENT=development + AUTH_OTP_DISABLED=true 에서만 열린다. + if AUTH_OTP_DISABLED: + logger.warning( + "[auth] 개발용 OTP 생략 — %s (%s)", + email, + "PERIODIC" if periodic_reverify else "NEW_BROWSER", + ) + else: + await _send_otp(user, "LOGIN", "로그인") + return { + "status": "otp_required", + "reason": "PERIODIC" if periodic_reverify else "NEW_BROWSER", + } return await _finish_login(user, agent, device_token_hash) diff --git a/A07_Register/A07_Register_Router.py b/A07_Register/A07_Register_Router.py index ca2d5b7b..51e49b61 100644 --- a/A07_Register/A07_Register_Router.py +++ b/A07_Register/A07_Register_Router.py @@ -1,5 +1,6 @@ """회원가입, 이메일 인증 및 회사 검색 API.""" +import logging from datetime import datetime from fastapi import APIRouter, HTTPException, Query, Request @@ -27,9 +28,12 @@ from common_util.common_util_auth_repository import ( ) from common_util.common_util_email import send_email_background from common_util.common_util_email_templates import otp_email +from config.config_system import AUTH_OTP_DISABLED from .A07_Register_Schema import RegisterRequest, RegisterVerifyRequest +logger = logging.getLogger(__name__) + router = APIRouter(prefix="/api/auth", tags=["Authentication"]) @@ -60,6 +64,10 @@ async def request_registration(payload: RegisterRequest): await replace_otp(user_id, "REGISTER", hash_password(code)) subject, html = otp_email(code, "회원가입") send_email_background(email, subject, html) + # 개발 단계에서는 메일을 못 받아도 가입을 이어갈 수 있게 코드를 로그에 찍는다 + # (2026-09-13 사용자 지시). ENVIRONMENT=development + AUTH_OTP_DISABLED=true 에서만. + if AUTH_OTP_DISABLED: + logger.warning("[auth] 개발용 가입 인증 코드 — %s : %s", email, code) return {"status": "success", "message": "인증 코드를 발송했습니다."} diff --git a/config/config_system.py b/config/config_system.py index 4a60494d..98ef486f 100644 --- a/config/config_system.py +++ b/config/config_system.py @@ -274,3 +274,10 @@ CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:5173,http://localhost # 9. 환경 정보 # ───────────────────────────────────────────────────────────────────────── ENVIRONMENT = os.getenv("ENVIRONMENT", "development") # development, staging, production + +# 개발 단계에서 로그인 이메일 인증(OTP)을 건너뛴다 (2026-09-13 사용자 지시). +# ⚠ 운영에서는 절대 켜지 않는다 — ENVIRONMENT 가 development 일 때만 듣는다. +# 가입 인증 코드는 건너뛰지 않고 서버 로그(tmp/server.log)에 찍어 메일 없이 진행한다. +AUTH_OTP_DISABLED = ( + os.getenv("AUTH_OTP_DISABLED", "False").lower() == "true" and ENVIRONMENT == "development" +)