Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POST binding support for authnrequest #422

Open
wants to merge 5 commits into
base: 3.4.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion settings_example.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// 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.
Expand Down
75 changes: 70 additions & 5 deletions src/Saml2/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
*/
Expand Down
51 changes: 51 additions & 0 deletions src/Saml2/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -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('<input type="hidden" name="%s" value="">', $name);
} else if (is_array($value)) {
foreach ($value as $val) {
$inputs .= sprintf('<input type="hidden" name="%s[]" value="%s">', $name, $val);
}
} else {
$inputs .= sprintf('<input type="hidden" name="%s" value="%s">', $name, $value);
}
}

printf('<html lang="en">
<head><title>samlPost</title></head>
<body>
<form name="samlPost" action="%s" method="post" enctype="application/x-www-form-urlencoded">%s</form>
<script>document.samlPost.submit();</script>
</body>
</html>', $url, $inputs);

}

/**
* Executes a redirection to the provided url (or return the target url).
*
Expand Down