From 554a5dc7bd07ca1621b21f0b1d5d3898b0ac9cea Mon Sep 17 00:00:00 2001 From: Fredrik Sundblom Date: Mon, 20 Jan 2020 16:34:28 +0100 Subject: [PATCH 1/5] Added POST binding support for the singleSignOnService endpoint --- settings_example.php | 2 +- src/Saml2/Auth.php | 16 ++++++++++++ src/Saml2/Utils.php | 58 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/settings_example.php b/settings_example.php index 981a21a3..4f14bcba 100644 --- a/settings_example.php +++ b/settings_example.php @@ -86,7 +86,7 @@ 'url' => '', // SAML protocol binding to be used when returning the // message. Onelogin Toolkit supports for this endpoint the - // HTTP-Redirect binding only + // HTTP-Redirect and HTTP-POST bindings 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', ), // SLO endpoint info of the IdP. diff --git a/src/Saml2/Auth.php b/src/Saml2/Auth.php index 5f603a17..929e375c 100644 --- a/src/Saml2/Auth.php +++ b/src/Saml2/Auth.php @@ -559,6 +559,11 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn $parameters['SigAlg'] = $security['signatureAlgorithm']; $parameters['Signature'] = $signature; } + + if ($this->getSSOBinding() === Constants::BINDING_HTTP_POST) { + return Utils::post($this->getSSOurl(), $parameters, $stay); + } + return $this->redirectTo($this->getSSOurl(), $parameters, $stay); } @@ -629,6 +634,17 @@ public function getSSOurl() return $idpData['singleSignOnService']['url']; } + /** + * Gets the SSO binding. + * + * @return string The binding of the Single Sign On Service + */ + public function getSSOBinding() + { + $idpData = $this->_settings->getIdPData(); + return $idpData['singleSignOnService']['binding']; + } + /** * Gets the SLO url. * diff --git a/src/Saml2/Utils.php b/src/Saml2/Utils.php index 50d3d41b..a2c10948 100644 --- a/src/Saml2/Utils.php +++ b/src/Saml2/Utils.php @@ -286,6 +286,64 @@ public static function getStringBetween($str, $start, $end) return substr($str, $ini, $len); } + /** + * Executes a post to the provided url. + * + * @param string $url The target url + * @param array $parameters Extra parameters to be passed as part of the url + * + * @throws Error + */ + public static function post($url, array $parameters = []) { + assert(is_string($url)); + + if (strpos($url, '/') === 0) { + $url = self::getSelfURLhost() . $url; + } + + /** + * Verify that the URL matches the regex for the protocol. + * By default this will check for http and https + */ + $wrongProtocol = !preg_match(self::$_protocolRegex, $url); + $url = filter_var($url, FILTER_VALIDATE_URL); + if ($wrongProtocol || empty($url)) { + throw new Error( + 'Post to invalid URL: ' . $url, + Error::REDIRECT_INVALID_URL + ); + } + + $inputs = ''; + foreach ($parameters as $name => $value) { + if ($value === null) { + $inputs .= sprintf('', $name); + } else if (is_array($value)) { + foreach ($value as $val) { + $inputs .= sprintf('', $name, $val); + } + } else { + $inputs .= sprintf('', $name, $value); + } + } + + printf(' + samlPost + +
+ %s +
+ + + ', + $url, + $inputs + ); + exit; + } + /** * Executes a redirection to the provided url (or return the target url). * From 8bd15238ebff147751e6495c281e4dedac422f7e Mon Sep 17 00:00:00 2001 From: Fredrik Sundblom Date: Mon, 20 Jan 2020 16:39:21 +0100 Subject: [PATCH 2/5] Better code readability.. --- src/Saml2/Utils.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Saml2/Utils.php b/src/Saml2/Utils.php index a2c10948..304d2603 100644 --- a/src/Saml2/Utils.php +++ b/src/Saml2/Utils.php @@ -330,17 +330,11 @@ public static function post($url, array $parameters = []) { printf(' samlPost -
- %s -
- +
%s
+ - ', - $url, - $inputs - ); + ', $url, $inputs); + exit; } From fe05453f98ccb39ced9a563c4d27d8e880370bcc Mon Sep 17 00:00:00 2001 From: Fredrik Sundblom Date: Tue, 21 Jan 2020 09:47:59 +0100 Subject: [PATCH 3/5] SAMLRequest shouldn't be deflated when using POST binding --- src/Saml2/Auth.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Saml2/Auth.php b/src/Saml2/Auth.php index 929e375c..80ee0327 100644 --- a/src/Saml2/Auth.php +++ b/src/Saml2/Auth.php @@ -544,7 +544,11 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn $this->_lastRequest = $authnRequest->getXML(); $this->_lastRequestID = $authnRequest->getId(); - $samlRequest = $authnRequest->getRequest(); + $deflate = null; + if ($this->getSSOBinding() === Constants::BINDING_HTTP_POST) { + $deflate = false; + } + $samlRequest = $authnRequest->getRequest($deflate); $parameters['SAMLRequest'] = $samlRequest; if (!empty($returnTo)) { From 2ffbb90eac4c0cb2d1a5641f6e6a72da7a57314f Mon Sep 17 00:00:00 2001 From: Fredrik Sundblom Date: Tue, 21 Jan 2020 09:48:29 +0100 Subject: [PATCH 4/5] Removed unused param --- src/Saml2/Auth.php | 3 ++- src/Saml2/Utils.php | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Saml2/Auth.php b/src/Saml2/Auth.php index 80ee0327..ac8fa87e 100644 --- a/src/Saml2/Auth.php +++ b/src/Saml2/Auth.php @@ -565,7 +565,8 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn } if ($this->getSSOBinding() === Constants::BINDING_HTTP_POST) { - return Utils::post($this->getSSOurl(), $parameters, $stay); + Utils::post($this->getSSOurl(), $parameters); + exit; } return $this->redirectTo($this->getSSOurl(), $parameters, $stay); diff --git a/src/Saml2/Utils.php b/src/Saml2/Utils.php index 304d2603..748106c1 100644 --- a/src/Saml2/Utils.php +++ b/src/Saml2/Utils.php @@ -335,7 +335,6 @@ public static function post($url, array $parameters = []) { ', $url, $inputs); - exit; } /** From 8d834d2375afa58d7c64700e9afae1f4ccaf1fec Mon Sep 17 00:00:00 2001 From: Fredrik Sundblom Date: Mon, 4 May 2020 16:48:51 +0200 Subject: [PATCH 5/5] Added support for signed of AuthnRequests --- src/Saml2/Auth.php | 52 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/Saml2/Auth.php b/src/Saml2/Auth.php index ac8fa87e..9e3e77ac 100644 --- a/src/Saml2/Auth.php +++ b/src/Saml2/Auth.php @@ -558,10 +558,24 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn } $security = $this->_settings->getSecurityData(); - if (isset($security['authnRequestsSigned']) && $security['authnRequestsSigned']) { - $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']); - $parameters['SigAlg'] = $security['signatureAlgorithm']; - $parameters['Signature'] = $signature; + if ($this->authnRequestsSigned()) { + switch ($this->getSSOBinding()) { + case Constants::BINDING_HTTP_REDIRECT: + $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], + $security['signatureAlgorithm']); + $parameters['SigAlg'] = $security['signatureAlgorithm']; + $parameters['Signature'] = $signature; + break; + + case Constants::BINDING_HTTP_POST: + $parameters['SAMLRequest'] = $this->buildEmbeddedSignature($authnRequest->getXML(), $security['signatureAlgorithm']); + break; + + default: + throw new Error(sprintf('Signing of AuthnRequests is unsupported for this binding (%s)', $this->getSSOBinding()), Error::UNSUPPORTED_SETTINGS_OBJECT); + break; + + } } if ($this->getSSOBinding() === Constants::BINDING_HTTP_POST) { @@ -572,6 +586,14 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn return $this->redirectTo($this->getSSOurl(), $parameters, $stay); } + /** + * @return bool + */ + protected function authnRequestsSigned() { + $security = $this->_settings->getSecurityData(); + return isset($security['authnRequestsSigned']) && $security['authnRequestsSigned']; + } + /** * Initiates the SLO process. * @@ -786,6 +808,28 @@ private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm return base64_encode($signature); } + /** + * @param string $samlMessage + * @param string $signAlgorithm + * @param bool $encode - Whether to base64 encode the signed message + * @return string The signed message + * @throws Error + */ + private function buildEmbeddedSignature($samlMessage, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $encode = true) { + $key = $this->_settings->getSPkey(); + if (empty($key)) { + throw new Error("Trying to embed signature in the SAML Request but can't load the SP private key", Error::PRIVATE_KEY_NOT_FOUND); + } + + $signedSamlMessage = Utils::addSign($samlMessage, $key, $this->_settings->getSPcert(), $signAlgorithm); + + if ($encode) { + return base64_encode($signedSamlMessage); + } + + return $signedSamlMessage; + } + /** * @return string The ID of the last message processed */