From 5c87778134d1fd3b0fb6584407dbeab8a76d7a75 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Mon, 5 Oct 2020 11:16:36 +0200 Subject: [PATCH] Fix login flow with SAML Because of the strict samesite cookies SAML fails with the login flow. Because the post that comes back is not transfering the proper cookies to use the same session. Hence the token in use gets lost etc. Now we store this all (encrypted) in a cookie. So that when we come back we can restore the proper session. FAQ: * Is it elegant? Nope! * Does it work? Yes! Signed-off-by: Roeland Jago Douma --- appinfo/info.xml | 2 +- lib/Controller/SAMLController.php | 38 +++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 6984bf3..b4ce1f8 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -33,7 +33,7 @@ While theoretically any other authentication provider implementing either one of https://raw.githubusercontent.com/nextcloud/user_saml/master/screenshots/1.png https://raw.githubusercontent.com/nextcloud/user_saml/master/screenshots/2.png - + OCA\User_SAML\Command\GetMetadata diff --git a/lib/Controller/SAMLController.php b/lib/Controller/SAMLController.php index 4040912..d04a60f 100644 --- a/lib/Controller/SAMLController.php +++ b/lib/Controller/SAMLController.php @@ -23,6 +23,8 @@ namespace OCA\User_SAML\Controller; use Firebase\JWT\JWT; +use OC\Core\Controller\ClientFlowLoginController; +use OC\Core\Controller\ClientFlowLoginV2Controller; use OCA\User_SAML\Exceptions\NoUserFoundException; use OCA\User_SAML\SAMLSettings; use OCA\User_SAML\UserBackend; @@ -184,11 +186,24 @@ class SAMLController extends Controller { $ssoUrl = $auth->login(null, [], false, false, true); $response = new Http\RedirectResponse($ssoUrl); + // Small hack to make user_saml work with the loginflows + $flowData = []; + + if ($this->session->get(ClientFlowLoginController::STATE_NAME) !== null) { + $flowData['cf1'] = $this->session->get(ClientFlowLoginController::STATE_NAME); + } else if ($this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME) !== null) { + $flowData['cf2'] = [ + 'name' => $this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME), + 'state' => $this->session->get(ClientFlowLoginV2Controller::STATE_NAME), + ]; + } + // Pack data as JSON so we can properly extract it later $data = json_encode([ 'AuthNRequestID' => $auth->getLastRequestID(), 'OriginalUrl' => $this->request->getParam('originalUrl', ''), - 'Idp' => $idp + 'Idp' => $idp, + 'flow' => $flowData, ]); // Encrypt it @@ -284,6 +299,15 @@ class SAMLController extends Controller { } $data = json_decode($cookie, true); + if (isset($data['flow'])) { + if (isset($data['flow']['cf1'])) { + $this->session->set(ClientFlowLoginController::STATE_NAME, $data['flow']['cf1']); + } else if (isset($data['flow']['cf2'])) { + $this->session->set(ClientFlowLoginV2Controller::TOKEN_NAME, $data['flow']['cf2']['token']); + $this->session->set(ClientFlowLoginV2Controller::STATE_NAME, $data['flow']['cf2']['state']); + } + + } $AuthNRequestID = $data['AuthNRequestID']; $idp = $data['Idp']; @@ -308,7 +332,9 @@ class SAMLController extends Controller { if (!$auth->isAuthenticated()) { $this->logger->info('Auth failed', ['app' => $this->appName]); - return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response->invalidateCookie('saml_data'); + return $response; } // Check whether the user actually exists, if not redirect to an error page @@ -317,7 +343,9 @@ class SAMLController extends Controller { $this->autoprovisionIfPossible($auth->getAttributes()); } catch (NoUserFoundException $e) { $this->logger->error($e->getMessage(), ['app' => $this->appName]); - return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response->invalidateCookie('saml_data'); + return $response; } $this->session->set('user_saml.samlUserData', $auth->getAttributes()); @@ -338,7 +366,9 @@ class SAMLController extends Controller { } } catch (\Exception $e) { $this->logger->logException($e, ['app' => $this->appName]); - return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); + $response->invalidateCookie('saml_data'); + return $response; } $originalUrl = $data['OriginalUrl'];