o2o-site-AEO/solution/backend/config/server_configs.py
Mina Choi 48109fdc99 [feat] solution/backend,frontend: id/pw 가입 · 구글 로그인 — 계정을 만들 길이 없던 걸 연다
계정 생성 API 가 아예 없었다(그동안 users 를 손으로 INSERT 했다). 로그인 화면은 있는데
그 뒤에 설 계정을 만들 방법이 제품에 없는 상태였다.

- auth_service.signup: 가입 = **새 회사(테넌트) 1개 + 첫 계정 1개**. users.company_id 가
  NOT NULL 이고 모든 도메인이 company 로 스코프돼서, 회사 없는 계정은 아무것도 못 만든다
- services/external/google_identity: 구글 ID 토큰의 서명·iss·만료에 더해 **aud(우리 client_id)와
  email_verified 를 본다.** aud 검사가 빠지면 남의 앱에 발급된 '진짜' 구글 토큰으로 우리 계정에
  들어온다 — 서명도 발급자도 전부 맞으므로 다른 검사로는 안 걸린다
- users.provider/provider_uid 추가, password NULL 허용, id 20→64자(google_<sub> 가 20자를 넘는다).
  provider 에 server_default 를 같이 준 이유: ORM default 는 raw INSERT(테스트 시드)에 안 먹어서
  NOT NULL 컬럼이면 그 경로가 통째로 깨진다
- attempt_login: 소셜 계정을 먼저 끊는다. 안 끊으면 bcrypt 가 None 해시를 만나 500 이다
- 같은 이메일이라도 id/pw 계정과 구글 계정을 **잇지 않는다.** 이으면 계정 선점이다 —
  남의 이메일로 먼저 만들어 둔 계정에 그 사람의 구글 로그인이 들어간다 → DECISIONS 1-5
- LoginPage 는 admin 과 공유라 selfServe 로 갈랐다. admin 은 가입 링크도 구글 버튼도 안 뜬다
  (admin 라우터에 /signup 이 없어 404 가 난다)
- GOOGLE_CLIENT_ID 는 루트 .env 한 곳. compose 가 VITE_GOOGLE_CLIENT_ID 로 흘려보낸다 —
  두 곳에 적으면 백엔드 aud 대조와 화면 버튼이 조용히 갈라진다

★ 이미 도는 DB 는 init.sql 을 다시 적용해야 한다(말미 ALTER 섹션).

pytest: auth 13건 + 구글 토큰 검증 8건(진짜 RSA 서명으로 aud·iss·만료·email_verified·변조
거절 확인) 통과. 전체 527 passed / 8 failed(전부 기존 실패, 인증과 무관).
tsc·eslint·vite build 통과.
2026-09-02 09:33:59 +09:00

31 lines
1.2 KiB
Python

"""설정 싱글턴 — 호출부 21개 파일이 이 이름들을 가져다 쓴다."""
from config.config_models import (
APP_ENV, # 재export — 테스트가 "지금 어느 환경으로 도는가" 를 여기서 읽는다(tests/test_config.py)
ExternalApiConfig,
GoogleOAuthConfig,
JwtToken,
LogConfig,
MainDBConfig,
WebServerConfig,
get_external_api_config,
get_google_oauth_config,
get_jwt_token_config,
get_log_config,
get_main_db_config,
get_web_server_config,
)
web_server_config: WebServerConfig = get_web_server_config()
log_config: LogConfig = get_log_config()
main_db_config: MainDBConfig = get_main_db_config()
jwt_token_config: JwtToken = get_jwt_token_config()
external_api_config: ExternalApiConfig = get_external_api_config()
google_oauth_config: GoogleOAuthConfig = get_google_oauth_config()
# 부팅은 막지 않는다 — 토큰이 필요 없는 로컬 작업까지 못 하게 되면 곤란하다.
if not jwt_token_config.access_key or not jwt_token_config.refresh_key:
from common.logger import LOG
LOG.w("[보안] JWT 시크릿이 비어 있습니다 — .env 에 JWT_ACCESS_SECRET / JWT_REFRESH_SECRET 를 넣으세요.")