Skip to content

Commit 5ae1f70

Browse files
Add support for Google OAuth Scheme Override (#7178)
* Added support for Google Oauth Scheme Override (through environment variable) * Refactoring * Refactoring * Applied formatting * Refactoring * Refactoring * Updated comment for `GOOGLE_OAUTH_SCHEME_OVERRIDE` variable * Updated comment for `GOOGLE_OAUTH_SCHEME_OVERRIDE` variable * Removed duplication of url_for function --------- Co-authored-by: kostiantyn-dementiev-op <[email protected]>
1 parent 3f781d2 commit 5ae1f70

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

redash/authentication/google_oauth.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from authlib.integrations.flask_client import OAuth
55
from flask import Blueprint, flash, redirect, request, session, url_for
66

7-
from redash import models
7+
from redash import models, settings
88
from redash.authentication import (
99
create_and_login_user,
1010
get_next_path,
@@ -29,40 +29,64 @@ def verify_profile(org, profile):
2929
return False
3030

3131

32+
def get_user_profile(access_token, logger):
33+
headers = {"Authorization": f"OAuth {access_token}"}
34+
response = requests.get("https://www.googleapis.com/oauth2/v1/userinfo", headers=headers)
35+
36+
if response.status_code == 401:
37+
logger.warning("Failed getting user profile (response code 401).")
38+
return None
39+
40+
return response.json()
41+
42+
43+
def build_redirect_uri():
44+
scheme = settings.GOOGLE_OAUTH_SCHEME_OVERRIDE or None
45+
return url_for(".callback", _external=True, _scheme=scheme)
46+
47+
48+
def build_next_path(org_slug=None):
49+
next_path = request.args.get("next")
50+
if not next_path:
51+
if org_slug is None:
52+
org_slug = session.get("org_slug")
53+
54+
scheme = None
55+
if settings.GOOGLE_OAUTH_SCHEME_OVERRIDE:
56+
scheme = settings.GOOGLE_OAUTH_SCHEME_OVERRIDE
57+
58+
next_path = url_for(
59+
"redash.index",
60+
org_slug=org_slug,
61+
_external=True,
62+
_scheme=scheme,
63+
)
64+
return next_path
65+
66+
3267
def create_google_oauth_blueprint(app):
3368
oauth = OAuth(app)
3469

3570
logger = logging.getLogger("google_oauth")
3671
blueprint = Blueprint("google_oauth", __name__)
3772

3873
CONF_URL = "https://accounts.google.com/.well-known/openid-configuration"
39-
oauth = OAuth(app)
4074
oauth.register(
4175
name="google",
4276
server_metadata_url=CONF_URL,
4377
client_kwargs={"scope": "openid email profile"},
4478
)
4579

46-
def get_user_profile(access_token):
47-
headers = {"Authorization": "OAuth {}".format(access_token)}
48-
response = requests.get("https://www.googleapis.com/oauth2/v1/userinfo", headers=headers)
49-
50-
if response.status_code == 401:
51-
logger.warning("Failed getting user profile (response code 401).")
52-
return None
53-
54-
return response.json()
55-
5680
@blueprint.route("/<org_slug>/oauth/google", endpoint="authorize_org")
5781
def org_login(org_slug):
5882
session["org_slug"] = current_org.slug
5983
return redirect(url_for(".authorize", next=request.args.get("next", None)))
6084

6185
@blueprint.route("/oauth/google", endpoint="authorize")
6286
def login():
63-
redirect_uri = url_for(".callback", _external=True)
87+
redirect_uri = build_redirect_uri()
6488

65-
next_path = request.args.get("next", url_for("redash.index", org_slug=session.get("org_slug")))
89+
next_path = build_next_path()
6690
logger.debug("Callback url: %s", redirect_uri)
6791
logger.debug("Next is: %s", next_path)
6892

@@ -86,7 +110,7 @@ def authorized():
86110
flash("Validation error. Please retry.")
87111
return redirect(url_for("redash.login"))
88112

89-
profile = get_user_profile(access_token)
113+
profile = get_user_profile(access_token, logger)
90114
if profile is None:
91115
flash("Validation error. Please retry.")
92116
return redirect(url_for("redash.login"))
@@ -110,7 +134,9 @@ def authorized():
110134
if user is None:
111135
return logout_and_redirect_to_index()
112136

113-
unsafe_next_path = session.get("next_url") or url_for("redash.index", org_slug=org.slug)
137+
unsafe_next_path = session.get("next_url")
138+
if not unsafe_next_path:
139+
unsafe_next_path = build_next_path(org.slug)
114140
next_path = get_next_path(unsafe_next_path)
115141

116142
return redirect(next_path)

redash/settings/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@
136136

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

139+
# If Redash is behind a proxy it might sometimes receive a X-Forwarded-Proto of HTTP
140+
# even if your actual Redash URL scheme is HTTPS. This will cause Flask to build
141+
# the OAuth redirect URL incorrectly thus failing auth. This is especially common if
142+
# you're behind a SSL/TCP configured AWS ELB or similar.
143+
# This setting will force the URL scheme.
144+
GOOGLE_OAUTH_SCHEME_OVERRIDE = os.environ.get("REDASH_GOOGLE_OAUTH_SCHEME_OVERRIDE", "")
145+
139146
GOOGLE_CLIENT_ID = os.environ.get("REDASH_GOOGLE_CLIENT_ID", "")
140147
GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")
141148
GOOGLE_OAUTH_ENABLED = bool(GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET)

0 commit comments

Comments
 (0)