[3/4] Implement SSO logout

Message ID 20200714133505.GA3451342@tsubame.mg0.fr
State New
Headers show
Series
  • [1/4] SSO: Explain the rationale behind prompt=login
Related show

Commit Message

Frédéric Mangano-Tarumi July 14, 2020, 1:35 p.m. UTC
---
 aurweb/routers/sso.py | 18 ++++++++++++++++++
 web/html/logout.php   | 14 +++++++++++++-
 2 files changed, 31 insertions(+), 1 deletion(-)

Patch

diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py
index e1ec7efe..a8d4b141 100644
--- a/aurweb/routers/sso.py
+++ b/aurweb/routers/sso.py
@@ -1,6 +1,8 @@ 
 import time
 import uuid
 
+from urllib.parse import urlencode
+
 import fastapi
 
 from authlib.integrations.starlette_client import OAuth
@@ -82,3 +84,19 @@  async def authenticate(request: Request, conn=Depends(aurweb.db.connect)):
     else:
         # We’ve got a severe integrity violation.
         raise Exception("Multiple accounts found for SSO account " + sub)
+
+
+@router.get("/sso/logout")
+async def logout():
+    """
+    Disconnect the user from the SSO provider, potentially affecting every
+    other Arch service. AUR logout is performed by `/logout`, before it
+    redirects to `/sso/logout`.
+
+    Based on the OpenID Connect Session Management specification:
+    https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
+    """
+    metadata = await oauth.sso.load_server_metadata()
+    # TODO Supply id_token_hint to the end session endpoint.
+    query = urlencode({'post_logout_redirect_uri': aurweb.config.get('options', 'aur_location')})
+    return RedirectResponse(metadata["end_session_endpoint"] + '?' + query)
diff --git a/web/html/logout.php b/web/html/logout.php
index 14022001..9fd63943 100644
--- a/web/html/logout.php
+++ b/web/html/logout.php
@@ -5,16 +5,28 @@  set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
 include_once("aur.inc.php");         # access AUR common functions
 include_once("acctfuncs.inc.php");         # access AUR common functions
 
+$redirect_uri = '/';
+
 # if they've got a cookie, log them out - need to do this before
 # sending any HTML output.
 #
 if (isset($_COOKIE["AURSID"])) {
+	$uid = uid_from_sid($_COOKIE['AURSID']);
 	delete_session_id($_COOKIE["AURSID"]);
 	# setting expiration to 1 means '1 second after midnight January 1, 1970'
 	setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
 	unset($_COOKIE['AURSID']);
 	clear_expired_sessions();
+
+	# If the account is linked to an SSO account, disconnect the user from the SSO too.
+	if (isset($uid)) {
+		$dbh = DB::connect();
+		$sso_account_id = $dbh->query("SELECT SSOAccountID FROM Users WHERE ID = " . $dbh->quote($uid))
+		                      ->fetchColumn();
+		if ($sso_account_id)
+			$redirect_uri = '/sso/logout';
+	}
 }
 
-header('Location: /');
+header("Location: $redirect_uri");