fix: set cookie domain for cross-subdomain authentication
In production, the session cookie needs to be shared across subdomains (e.g., api.example.com and app.example.com). - Add cookie_domain property to config (extracts parent domain) - Set SameSite=None for cross-origin requests in production - Update auth callback and logout to use cookie domain - This fixes the login loop where session cookie wasn't sent
This commit is contained in:
@@ -114,9 +114,18 @@ async def callback(
|
||||
cookie_options = build_cookie_options(settings)
|
||||
cookie_samesite = cast(Literal["lax", "strict", "none"], cookie_options["samesite"])
|
||||
cookie_secure = bool(cookie_options["secure"])
|
||||
response.set_cookie("session", session_cookie, httponly=True, samesite=cookie_samesite, secure=cookie_secure)
|
||||
response.delete_cookie("auth_state", samesite="lax")
|
||||
response.delete_cookie("auth_next", samesite="lax")
|
||||
cookie_domain = str(cookie_options["domain"]) if cookie_options.get("domain") else None
|
||||
|
||||
response.set_cookie(
|
||||
"session",
|
||||
session_cookie,
|
||||
httponly=True,
|
||||
samesite=cookie_samesite,
|
||||
secure=cookie_secure,
|
||||
domain=cookie_domain,
|
||||
)
|
||||
response.delete_cookie("auth_state", samesite="lax", domain=cookie_domain)
|
||||
response.delete_cookie("auth_next", samesite="lax", domain=cookie_domain)
|
||||
|
||||
logger.info("Auth callback complete for user id=%s, redirecting to %s", user.id, auth_next)
|
||||
|
||||
@@ -131,8 +140,9 @@ async def logout(response: Response) -> dict[str, str]:
|
||||
cookie_options = build_cookie_options(settings)
|
||||
cookie_samesite = cast(Literal["lax", "strict", "none"], cookie_options["samesite"])
|
||||
cookie_secure = bool(cookie_options["secure"])
|
||||
cookie_domain = str(cookie_options["domain"]) if cookie_options.get("domain") else None
|
||||
|
||||
response.delete_cookie("session", samesite=cookie_samesite, secure=cookie_secure)
|
||||
response.delete_cookie("session", samesite=cookie_samesite, secure=cookie_secure, domain=cookie_domain)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from src.config import Settings
|
||||
|
||||
|
||||
def build_cookie_options(settings: Settings) -> dict[str, str | bool]:
|
||||
def build_cookie_options(settings: Settings) -> dict[str, str | bool | None]:
|
||||
return {
|
||||
"httponly": True,
|
||||
"secure": settings.cookie_secure,
|
||||
"samesite": settings.cookie_samesite,
|
||||
"domain": settings.cookie_domain,
|
||||
}
|
||||
|
||||
+17
-1
@@ -117,6 +117,22 @@ class Settings(BaseSettings):
|
||||
@property
|
||||
def cookie_samesite(self) -> str:
|
||||
if self.app_env == "production":
|
||||
return "strict"
|
||||
return "none"
|
||||
|
||||
return "lax"
|
||||
|
||||
@property
|
||||
def cookie_domain(self) -> str | None:
|
||||
"""Return the parent domain for cross-subdomain cookies.
|
||||
|
||||
E.g., api.example.com and app.example.com both share .example.com
|
||||
"""
|
||||
if self.app_env != "production":
|
||||
return None
|
||||
|
||||
# Extract parent domain from api_domain
|
||||
# e.g., "api.headquarter.commumedia.org" -> ".headquarter.commumedia.org"
|
||||
parts = self.api_domain.split(".")
|
||||
if len(parts) >= 3:
|
||||
return "." + ".".join(parts[1:])
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user