chore(auth): 개발 단계 이메일 인증 생략 스위치 추가

개발 중 로그인마다 인증 코드를 기다리던 것을 끄는 스위치.
AUTH_OTP_DISABLED 가 true 이고 ENVIRONMENT 가 development 일 때만 듣는다 —
운영에서는 켜져 있어도 무시된다.

- 로그인: 새 브라우저·주기 재인증 둘 다 코드 없이 통과, 생략마다 경고 로그
- 회원가입: 흐름은 그대로 두고 인증 코드를 서버 로그에 기록
- .env 에 AUTH_OTP_DISABLED=True (배포 전 False 로 되돌릴 것)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJC56e4osweKJ4vafm9ReM
This commit is contained in:
2026-09-13 11:32:22 +09:00
co-authored by Claude Opus 5
parent 62dc2d744c
commit 1230961c54
4 changed files with 37 additions and 6 deletions
+4
View File
@@ -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
+18 -6
View File
@@ -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)
+8
View File
@@ -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": "인증 코드를 발송했습니다."}
+7
View File
@@ -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"
)