feat: OMS(orderlist)와 SSO 호환되도록 세션 계약 정렬
- 세션 쿠키 이름을 SESSION_COOKIE_NAME env 로 OMS 와 통일 - 로그인 성공 시 top-level user_email / user_name 키도 함께 저장 - get_user() 가 OMS 가 만든 SSO 세션(top-level email만 있는 경우)도 인정 - /login 에 ?next= 지원 (open redirect 방지 위해 같은 호스트 절대경로만 허용)
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
GOOGLE_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com
|
GOOGLE_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com
|
||||||
GOOGLE_CLIENT_SECRET=your-google-oauth-client-secret
|
GOOGLE_CLIENT_SECRET=your-google-oauth-client-secret
|
||||||
|
# OMS(orderlist) 와 SSO 하려면 양쪽 .env 에 동일한 값으로 설정
|
||||||
SESSION_SECRET_KEY=replace-with-a-long-random-secret
|
SESSION_SECRET_KEY=replace-with-a-long-random-secret
|
||||||
|
SESSION_COOKIE_NAME=session
|
||||||
SESSION_COOKIE_SECURE=true
|
SESSION_COOKIE_SECURE=true
|
||||||
PUBLIC_BASE_URL=https://dbx.no1king.freeddns.org
|
PUBLIC_BASE_URL=https://dbx.no1king.freeddns.org
|
||||||
CS_ORDER_URL=https://cs.example.com
|
CS_ORDER_URL=https://cs.example.com
|
||||||
|
|||||||
+38
-4
@@ -11,6 +11,9 @@ from fastapi.staticfiles import StaticFiles
|
|||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
|
|
||||||
|
# OMS(orderlist) 와 공유하는 세션 키. SessionMiddleware 의 session_cookie 도 동일 이름.
|
||||||
|
SESSION_COOKIE_DEFAULT = "session"
|
||||||
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -45,9 +48,11 @@ app = FastAPI(title="DBX 메인 페이지")
|
|||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
SessionMiddleware,
|
SessionMiddleware,
|
||||||
secret_key=env("SESSION_SECRET_KEY", "change-this-session-secret"),
|
secret_key=env("SESSION_SECRET_KEY", "change-this-session-secret"),
|
||||||
|
session_cookie=env("SESSION_COOKIE_NAME", SESSION_COOKIE_DEFAULT),
|
||||||
https_only=env("SESSION_COOKIE_SECURE", "true").lower() == "true",
|
https_only=env("SESSION_COOKIE_SECURE", "true").lower() == "true",
|
||||||
same_site="lax",
|
same_site="lax",
|
||||||
max_age=60 * 60 * 8,
|
max_age=60 * 60 * 8,
|
||||||
|
path="/",
|
||||||
)
|
)
|
||||||
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")
|
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")
|
||||||
|
|
||||||
@@ -64,7 +69,26 @@ def public_url_for(request: Request, route_name: str) -> str:
|
|||||||
|
|
||||||
def get_user(request: Request) -> dict[str, Any] | None:
|
def get_user(request: Request) -> dict[str, Any] | None:
|
||||||
user = request.session.get("user")
|
user = request.session.get("user")
|
||||||
return user if isinstance(user, dict) else None
|
if isinstance(user, dict):
|
||||||
|
return user
|
||||||
|
# OMS 가 top-level user_email 만 채워놓은 SSO 세션도 인정
|
||||||
|
email = request.session.get("user_email")
|
||||||
|
if email:
|
||||||
|
return {
|
||||||
|
"email": str(email),
|
||||||
|
"name": request.session.get("user_name") or str(email),
|
||||||
|
"picture": "",
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def safe_next(raw: str | None) -> str:
|
||||||
|
"""Open redirect 방지: 같은 호스트의 절대 경로만 허용."""
|
||||||
|
if not raw:
|
||||||
|
return "/"
|
||||||
|
if not raw.startswith("/") or raw.startswith("//") or raw.startswith("/\\"):
|
||||||
|
return "/"
|
||||||
|
return raw
|
||||||
|
|
||||||
|
|
||||||
def render_template(
|
def render_template(
|
||||||
@@ -140,6 +164,9 @@ async def login(request: Request):
|
|||||||
status_code=500,
|
status_code=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# SSO: ?next= 로 들어온 목적지를 OAuth 콜백 뒤에 사용하기 위해 세션에 임시 보관
|
||||||
|
request.session["_post_login_next"] = safe_next(request.query_params.get("next"))
|
||||||
|
|
||||||
redirect_uri = public_url_for(request, "auth_google")
|
redirect_uri = public_url_for(request, "auth_google")
|
||||||
return await oauth.google.authorize_redirect(
|
return await oauth.google.authorize_redirect(
|
||||||
request,
|
request,
|
||||||
@@ -174,12 +201,19 @@ async def auth_google(request: Request):
|
|||||||
status_code=403,
|
status_code=403,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
email = str(userinfo.get("email", "")).lower().strip()
|
||||||
|
name = userinfo.get("name") or email
|
||||||
|
# OMS 와 공유하는 top-level 키 (SSO 계약)
|
||||||
|
request.session["user_email"] = email
|
||||||
|
request.session["user_name"] = name
|
||||||
|
# 기존 코드/템플릿 호환용 dict
|
||||||
request.session["user"] = {
|
request.session["user"] = {
|
||||||
"email": str(userinfo.get("email", "")).lower().strip(),
|
"email": email,
|
||||||
"name": userinfo.get("name") or userinfo.get("email"),
|
"name": name,
|
||||||
"picture": userinfo.get("picture", ""),
|
"picture": userinfo.get("picture", ""),
|
||||||
}
|
}
|
||||||
return RedirectResponse(url="/", status_code=303)
|
next_url = safe_next(request.session.pop("_post_login_next", "/"))
|
||||||
|
return RedirectResponse(url=next_url, status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/logout")
|
@app.get("/logout")
|
||||||
|
|||||||
Reference in New Issue
Block a user