Redirect to referer after SSO login

Message ID 20200729152544.GA660646@tsubame.mg0.fr
State New
Headers show
Series Redirect to referer after SSO login | expand

Commit Message

Frédéric Mangano-Tarumi July 29, 2020, 3:25 p.m. UTC
Introduce a `redirect` query argument to SSO login endpoints so that
users are redirected to the page they were originally on when they
clicked the Login link.
---
 aurweb/routers/sso.py | 23 +++++++++++++++++------
 web/html/login.php    | 18 ++++++++++++------
 2 files changed, 29 insertions(+), 12 deletions(-)

Comments

Lukas Fleischer Aug. 2, 2020, 1:33 p.m. UTC | #1
On Wed, 29 Jul 2020 at 11:25:44, Frédéric Mangano-Tarumi wrote:
> Introduce a `redirect` query argument to SSO login endpoints so that
> users are redirected to the page they were originally on when they
> clicked the Login link.
> ---
>  aurweb/routers/sso.py | 23 +++++++++++++++++------
>  web/html/login.php    | 18 ++++++++++++------
>  2 files changed, 29 insertions(+), 12 deletions(-)

Thanks! I merged the patch into pu but we need to be careful here.

It probably makes sense to have all the code go through a security
review before we enable this feature in production in any case, so I am
not too worried now.

Patch

diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py
index 2e4fbacc..4ef31b0a 100644
--- a/aurweb/routers/sso.py
+++ b/aurweb/routers/sso.py
@@ -30,16 +30,21 @@  oauth.register(
 
 
 @router.get("/sso/login")
-async def login(request: Request):
+async def login(request: Request, redirect: str = None):
     """
     Redirect the user to the SSO provider’s login page.
 
     We specify prompt=login to force the user to input their credentials even
     if they’re already logged on the SSO. This is less practical, but given AUR
     has the potential to impact many users, better safe than sorry.
+
+    The `redirect` argument is a query parameter specifying the post-login
+    redirect URL.
     """
-    redirect_uri = aurweb.config.get("options", "aur_location") + "/sso/authenticate"
-    return await oauth.sso.authorize_redirect(request, redirect_uri, prompt="login")
+    authenticate_url = aurweb.config.get("options", "aur_location") + "/sso/authenticate"
+    if redirect:
+        authenticate_url = authenticate_url + "?" + urlencode([("redirect", redirect)])
+    return await oauth.sso.authorize_redirect(request, authenticate_url, prompt="login")
 
 
 def is_account_suspended(conn, user_id):
@@ -82,8 +87,15 @@  def is_ip_banned(conn, ip):
     return result.fetchone() is not None
 
 
+def is_aur_url(url):
+    aur_location = aurweb.config.get("options", "aur_location")
+    if not aur_location.endswith("/"):
+        aur_location = aur_location + "/"
+    return url.startswith(aur_location)
+
+
 @router.get("/sso/authenticate")
-async def authenticate(request: Request, conn=Depends(aurweb.db.connect)):
+async def authenticate(request: Request, redirect: str = None, conn=Depends(aurweb.db.connect)):
     """
     Receive an OpenID Connect ID token, validate it, then process it to create
     an new AUR session.
@@ -118,8 +130,7 @@  async def authenticate(request: Request, conn=Depends(aurweb.db.connect)):
         return "Sorry, we don’t seem to know you Sir " + sub
     elif len(aur_accounts) == 1:
         sid = open_session(request, conn, aur_accounts[0][Users.c.ID])
-        response = RedirectResponse("/")
-        # TODO redirect to the referrer
+        response = RedirectResponse(redirect if redirect and is_aur_url(redirect) else "/")
         response.set_cookie(key="AURSID", value=sid, httponly=True,
                             secure=request.url.scheme == "https")
         if "id_token" in token:
diff --git a/web/html/login.php b/web/html/login.php
index 3a146f60..3f3d66cc 100644
--- a/web/html/login.php
+++ b/web/html/login.php
@@ -9,6 +9,10 @@  if (!$disable_http_login || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])) {
 	$login_error = $login['error'];
 }
 
+$referer = in_request('referer');
+if ($referer === '')
+	$referer = $_SERVER['HTTP_REFERER'];
+
 html_header('AUR ' . __("Login"));
 ?>
 <div id="dev-login" class="box">
@@ -40,13 +44,15 @@  html_header('AUR ' . __("Login"));
 			<p>
 				<input type="submit" class="button" value="<?php  print __("Login"); ?>" />
 				<a href="<?= get_uri('/passreset/') ?>">[<?= __('Forgot Password') ?>]</a>
-				<?php if (config_get('sso', 'openid_configuration')): ?>
-				<a href="<?= get_uri('/sso/login') ?>">[<?= __('Login through SSO') ?>]</a>
+				<?php if (config_get('sso', 'openid_configuration')):
+					$sso_login_url = get_uri('/sso/login');
+					if (isset($referer))
+						$sso_login_url .= '?redirect=' . urlencode($referer);
+				?>
+				<a href="<?= htmlspecialchars($sso_login_url, ENT_QUOTES) ?>">[<?= __('Login through SSO') ?>]</a>
 				<?php endif; ?>
-				<?php if (in_request('referer') !== ""): ?>
-				<input id="id_referer" type="hidden" name="referer" value="<?= htmlspecialchars(in_request('referer'), ENT_QUOTES) ?>" />
-				<?php elseif (isset($_SERVER['HTTP_REFERER'])): ?>
-				<input id="id_referer" type="hidden" name="referer" value="<?= htmlspecialchars($_SERVER['HTTP_REFERER'], ENT_QUOTES) ?>" />
+				<?php if (isset($referer)): ?>
+				<input id="id_referer" type="hidden" name="referer" value="<?= htmlspecialchars($referer, ENT_QUOTES) ?>" />
 				<?php endif; ?>
 			</p>
 		</fieldset>