Seth commited on
Commit ·
168b3d6
1
Parent(s): e6a3185
update
Browse files- Dockerfile +2 -1
- SETUP.md +3 -1
- backend/app/auth_routes.py +40 -5
Dockerfile
CHANGED
|
@@ -29,4 +29,5 @@ EXPOSE 7860
|
|
| 29 |
# Set PYTHONPATH to include backend directory
|
| 30 |
ENV PYTHONPATH=/app/backend:$PYTHONPATH
|
| 31 |
|
| 32 |
-
|
|
|
|
|
|
| 29 |
# Set PYTHONPATH to include backend directory
|
| 30 |
ENV PYTHONPATH=/app/backend:$PYTHONPATH
|
| 31 |
|
| 32 |
+
# --proxy-headers: X-Forwarded-Proto/Host from Hugging Face edge so OAuth redirect_uri is https://
|
| 33 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--app-dir", "/app/backend", "--proxy-headers"]
|
SETUP.md
CHANGED
|
@@ -61,7 +61,9 @@
|
|
| 61 |
export SESSION_SECRET=$(openssl rand -hex 32)
|
| 62 |
```
|
| 63 |
|
| 64 |
-
For
|
|
|
|
|
|
|
| 65 |
|
| 66 |
3. **Run Backend**:
|
| 67 |
```bash
|
|
|
|
| 61 |
export SESSION_SECRET=$(openssl rand -hex 32)
|
| 62 |
```
|
| 63 |
|
| 64 |
+
For **Hugging Face Spaces**, set `FRONTEND_ORIGIN` to **`https://`**, e.g. `https://your-space.hf.space` (not `http://`). Google’s OAuth policy rejects `http://` redirect URIs for public hosts; the **Authorized redirect URI** in Google Cloud must be the **https** callback, e.g. `https://your-space.hf.space/api/auth/google/callback`.
|
| 65 |
+
|
| 66 |
+
For HTTPS deployments, also set `HTTPS_ONLY_COOKIES=1` and add your public origin to `CORS_ORIGINS` if the browser origin differs from the API host.
|
| 67 |
|
| 68 |
3. **Run Backend**:
|
| 69 |
```bash
|
backend/app/auth_routes.py
CHANGED
|
@@ -25,22 +25,57 @@ def _client_configured() -> bool:
|
|
| 25 |
return bool(os.environ.get("GOOGLE_CLIENT_ID", "").strip() and os.environ.get("GOOGLE_CLIENT_SECRET", "").strip())
|
| 26 |
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
def _redirect_uri(request: Request) -> str:
|
| 29 |
"""Callback URL; must match an entry in Google Cloud Console → OAuth client."""
|
| 30 |
explicit = os.environ.get("GOOGLE_REDIRECT_URI", "").strip()
|
| 31 |
if explicit:
|
| 32 |
-
return explicit
|
| 33 |
fe = os.environ.get("FRONTEND_ORIGIN", "").strip()
|
| 34 |
if fe:
|
| 35 |
-
return
|
| 36 |
-
return
|
| 37 |
|
| 38 |
|
| 39 |
def _post_login_url(request: Request) -> str:
|
| 40 |
fe = os.environ.get("FRONTEND_ORIGIN", "").strip()
|
| 41 |
if fe:
|
| 42 |
-
return
|
| 43 |
-
return
|
| 44 |
|
| 45 |
|
| 46 |
@router.get("/status")
|
|
|
|
| 25 |
return bool(os.environ.get("GOOGLE_CLIENT_ID", "").strip() and os.environ.get("GOOGLE_CLIENT_SECRET", "").strip())
|
| 26 |
|
| 27 |
|
| 28 |
+
def _normalize_https_public_origin(origin: str) -> str:
|
| 29 |
+
"""Spaces like Hugging Face are always HTTPS; fix http:// saved by mistake or bad defaults."""
|
| 30 |
+
o = origin.strip().rstrip("/")
|
| 31 |
+
if o.startswith("http://") and ".hf.space" in o:
|
| 32 |
+
return "https://" + o[7:]
|
| 33 |
+
return o
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _public_request_base(request: Request) -> str:
|
| 37 |
+
"""
|
| 38 |
+
Browser-facing origin (scheme + host, no path). Uses proxy headers so OAuth redirect_uri
|
| 39 |
+
is https://... behind Hugging Face / nginx (request.base_url alone is often http:// internally).
|
| 40 |
+
"""
|
| 41 |
+
fwd_proto = request.headers.get("x-forwarded-proto")
|
| 42 |
+
if fwd_proto:
|
| 43 |
+
proto = fwd_proto.split(",")[0].strip().lower()
|
| 44 |
+
else:
|
| 45 |
+
proto = (request.url.scheme or "http").lower()
|
| 46 |
+
|
| 47 |
+
fwd_host = request.headers.get("x-forwarded-host")
|
| 48 |
+
if fwd_host:
|
| 49 |
+
host = fwd_host.split(",")[0].strip()
|
| 50 |
+
else:
|
| 51 |
+
host = request.headers.get("host") or request.url.netloc or ""
|
| 52 |
+
|
| 53 |
+
if host:
|
| 54 |
+
# Use Host / X-Forwarded-Host as-is (keeps localhost:5173 for dev proxy).
|
| 55 |
+
base = f"{proto}://{host}" if proto else f"https://{host}"
|
| 56 |
+
base = _normalize_https_public_origin(base)
|
| 57 |
+
return base.rstrip("/")
|
| 58 |
+
|
| 59 |
+
base = str(request.base_url).rstrip("/")
|
| 60 |
+
return _normalize_https_public_origin(base)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
def _redirect_uri(request: Request) -> str:
|
| 64 |
"""Callback URL; must match an entry in Google Cloud Console → OAuth client."""
|
| 65 |
explicit = os.environ.get("GOOGLE_REDIRECT_URI", "").strip()
|
| 66 |
if explicit:
|
| 67 |
+
return _normalize_https_public_origin(explicit)
|
| 68 |
fe = os.environ.get("FRONTEND_ORIGIN", "").strip()
|
| 69 |
if fe:
|
| 70 |
+
return _normalize_https_public_origin(fe) + "/api/auth/google/callback"
|
| 71 |
+
return _public_request_base(request) + "/api/auth/google/callback"
|
| 72 |
|
| 73 |
|
| 74 |
def _post_login_url(request: Request) -> str:
|
| 75 |
fe = os.environ.get("FRONTEND_ORIGIN", "").strip()
|
| 76 |
if fe:
|
| 77 |
+
return _normalize_https_public_origin(fe) + "/"
|
| 78 |
+
return _public_request_base(request) + "/"
|
| 79 |
|
| 80 |
|
| 81 |
@router.get("/status")
|