Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Google OAuth Scheme Override #7178

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
76 changes: 61 additions & 15 deletions redash/authentication/google_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from authlib.integrations.flask_client import OAuth
from flask import Blueprint, flash, redirect, request, session, url_for

from redash import models
from redash import models, settings
from redash.authentication import (
create_and_login_user,
get_next_path,
Expand All @@ -29,6 +29,48 @@ def verify_profile(org, profile):
return False


def get_user_profile(access_token, logger):
headers = {"Authorization": f"OAuth {access_token}"}
response = requests.get("https://www.googleapis.com/oauth2/v1/userinfo", headers=headers)

if response.status_code == 401:
logger.warning("Failed getting user profile (response code 401).")
return None

return response.json()


def build_redirect_uri():
if settings.GOOGLE_OAUTH_SCHEME_OVERRIDE:
redirect_uri = url_for(
".callback",
_external=True,
_scheme=settings.GOOGLE_OAUTH_SCHEME_OVERRIDE,
)
else:
redirect_uri = url_for(".callback", _external=True)
return redirect_uri


def build_next_path():
next_path = request.args.get("next")
if not next_path:
if settings.GOOGLE_OAUTH_SCHEME_OVERRIDE:
next_path = url_for(
"redash.index",
org_slug=session.get("org_slug"),
_external=True,
_scheme=settings.GOOGLE_OAUTH_SCHEME_OVERRIDE,
)
else:
next_path = url_for(
"redash.index",
org_slug=session.get("org_slug"),
_external=True,
)
return next_path


def create_google_oauth_blueprint(app):
oauth = OAuth(app)

Expand All @@ -43,26 +85,16 @@ def create_google_oauth_blueprint(app):
client_kwargs={"scope": "openid email profile"},
)

def get_user_profile(access_token):
headers = {"Authorization": "OAuth {}".format(access_token)}
response = requests.get("https://www.googleapis.com/oauth2/v1/userinfo", headers=headers)

if response.status_code == 401:
logger.warning("Failed getting user profile (response code 401).")
return None

return response.json()

@blueprint.route("/<org_slug>/oauth/google", endpoint="authorize_org")
def org_login(org_slug):
session["org_slug"] = current_org.slug
return redirect(url_for(".authorize", next=request.args.get("next", None)))

@blueprint.route("/oauth/google", endpoint="authorize")
def login():
redirect_uri = url_for(".callback", _external=True)
redirect_uri = build_redirect_uri()

next_path = request.args.get("next", url_for("redash.index", org_slug=session.get("org_slug")))
next_path = build_next_path()
logger.debug("Callback url: %s", redirect_uri)
logger.debug("Next is: %s", next_path)

Expand All @@ -86,7 +118,7 @@ def authorized():
flash("Validation error. Please retry.")
return redirect(url_for("redash.login"))

profile = get_user_profile(access_token)
profile = get_user_profile(access_token, logger)
if profile is None:
flash("Validation error. Please retry.")
return redirect(url_for("redash.login"))
Expand All @@ -110,7 +142,21 @@ def authorized():
if user is None:
return logout_and_redirect_to_index()

unsafe_next_path = session.get("next_url") or url_for("redash.index", org_slug=org.slug)
unsafe_next_path = session.get("next_url")
if not unsafe_next_path:
if settings.GOOGLE_OAUTH_SCHEME_OVERRIDE:
unsafe_next_path = url_for(
"redash.index",
org_slug=org.slug,
_external=True,
_scheme=settings.GOOGLE_OAUTH_SCHEME_OVERRIDE,
)
else:
unsafe_next_path = url_for(
"redash.index",
org_slug=org.slug,
_external=True,
)
next_path = get_next_path(unsafe_next_path)

return redirect(next_path)
Expand Down
7 changes: 7 additions & 0 deletions redash/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@

MULTI_ORG = parse_boolean(os.environ.get("REDASH_MULTI_ORG", "false"))

# If Redash is behind a proxy it might sometimes receive a X-Forwarded-Proto of HTTP
# even if your actual Redash URL scheme is HTTPS. This will cause Flask to build
# the OAuth redirect URL incorrectly thus failing auth. This is especially common if
# you're behind a SSL/TCP configured AWS ELB or similar.
# This setting will force the URL scheme.
GOOGLE_OAUTH_SCHEME_OVERRIDE = os.environ.get("REDASH_GOOGLE_OAUTH_SCHEME_OVERRIDE", "")

GOOGLE_CLIENT_ID = os.environ.get("REDASH_GOOGLE_CLIENT_ID", "")
GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")
GOOGLE_OAUTH_ENABLED = bool(GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET)
Expand Down
Loading