Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions flask-server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ CANCERVERSE_LOWRES_PATH=/home/visitor/cancerverse_lowres
# Defaults to http://localhost:5173 for dev.
# FRONTEND_URL=https://bodymaps.wse.jhu.edu

# The app's public origin, used to build the OAuth redirect_uri that providers
# match as an exact string. REQUIRED in production: derived from the request it
# would depend on the proxy chain reporting the scheme correctly, and an
# http:// value makes every sign-in fail. Leave unset in dev.
# PUBLIC_BASE_URL=https://bodymaps.wse.jhu.edu

# --- OAuth providers (optional) ---
# Leave unset to disable a provider; its button stays disabled in the UI and
# /api/auth/oauth/providers reports it as unavailable.
Expand Down
16 changes: 12 additions & 4 deletions flask-server/api/oauth_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ def _frontend_url() -> str:
return os.environ.get("FRONTEND_URL", "http://localhost:5173")


# The provider matches redirect_uri as an exact string, so it has to be the
# app's real public origin. Deriving it from the request means trusting the
# proxy chain to report scheme/host correctly, which isn't always in our
# control — an nginx that rewrites X-Forwarded-Proto yields http:// and every
# sign-in fails. Set PUBLIC_BASE_URL in production to state it outright;
# unset, we fall back to the request (correct for local dev).
def _callback_url(provider: str) -> str:
base = os.environ.get("PUBLIC_BASE_URL") or request.url_root
return urljoin(base.rstrip("/") + "/", f"api/auth/oauth/{provider}/callback")


def _provider_configured(provider: str) -> bool:
return bool(
os.environ.get(f"{provider.upper()}_CLIENT_ID")
Expand Down Expand Up @@ -89,10 +100,7 @@ def oauth_start(provider):
return jsonify({"error": f"{provider} sign-in isn't configured"}), 503

client = oauth.create_client(provider)
# Build the absolute callback URL from this request so it matches whatever
# host/port the app is actually served on.
redirect_uri = urljoin(request.url_root, f"api/auth/oauth/{provider}/callback")
return client.authorize_redirect(redirect_uri)
return client.authorize_redirect(_callback_url(provider))


@oauth_blueprint.route("/auth/oauth/<provider>/callback", methods=["GET"])
Expand Down
Loading