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..9e3e77ac 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)) { @@ -554,14 +558,42 @@ 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) { + Utils::post($this->getSSOurl(), $parameters); + exit; + } + 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. * @@ -629,6 +661,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. * @@ -765,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 */ diff --git a/src/Saml2/Utils.php b/src/Saml2/Utils.php index 50d3d41b..748106c1 100644 --- a/src/Saml2/Utils.php +++ b/src/Saml2/Utils.php @@ -286,6 +286,57 @@ 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); + + } + /** * Executes a redirection to the provided url (or return the target url). *