Open alexsenatore opened 9 years ago
Hi @alexsenatore!
Thanks for your suggestion.
I'm afraid implementing what you are asking for is not that simple. First of all, note that what you modified is not SimpleSAMLphp, but its sister SAML2 library. Then, you modified the HTTPPost
class, which is actually one single implementation of the many SAML 2.0 bindings supported. This should therefore be implemented for every binding, given that you may receive responses on any of them if you have configured your SSP adequately. HTTP-post is the most common binding, but the rest of them should also be supported.
The problem with other bindings is that the response may not be immediately available to you, or may require additional steps (as in the artifact binding, where you get an artifact you can use to query the IdP for the response in the back channel). In any case, you would need to implement a transversal way to obtain the response from any binding.
There's also the issue about what you need to do next with the response. The library creates a DOM object as soon as possible with the response, and you can't use that since the DOM interface is not serializable and therefore cannot be saved in the state array for future iterations. So you would need to capture the string very early, and keep it in the state array for a while.
Finally, you have to consider whether you actually need the response or the assertion. SAML responses are targeted to a specific recipient (the service provider), and therefore they are not intended to be carried forward. The meaningful part here is the SAML assertion, which contains information about a certain subject. Assertions can have their audience restricted, but that could be according to your setup, so that the audience intended for the assertion is the final OAuth2 consumer and your SAML service provider ignores it completely (it could even be encrypted, making it impossible for the SP to see it). I'm definitely not an expert on OAuth, but I think you'd want to pass the assertion rather than the response...
In any case, contributions are always welcome, and if you can provide a pull request with a complete implementation we could of course discuss the specifics and see if this is reasonable or not.
Hey. I recently had the same problem. I received a reply from ADFS and I had to delegate request (ActAs). I needed a Security Token logged-in user (Assertion). I changed the 2 functions. Security Token for user is saved to session variable.
\saml\simplesamlphp-1.13.2\lib\SimpleSAML\Utils\XML.php
public static function debugSAMLMessage($message, $type) {
if (!(is_string($type) && (is_string($message) || $message instanceof \DOMElement))) {
throw new \InvalidArgumentException('Invalid input parameters.');
}
$globalConfig = \SimpleSAML_Configuration::getInstance();
if ($message instanceof \DOMElement) {
$message = $message->ownerDocument->saveXML($message);
}
switch ($type) {
case 'in':
\SimpleSAML_Logger::debug('Received message:');
break;
case 'out':
\SimpleSAML_Logger::debug('Sending message:');
break;
case 'decrypt':
\SimpleSAML_Logger::debug('Decrypted message:');
break;
case 'encrypt':
\SimpleSAML_Logger::debug('Encrypted message:');
break;
default:
assert(false);
}
$str = self::formatXMLString($message);
foreach (explode("\n", $str) as $line) {
if (!$globalConfig->getBoolean('debug', true)) {
\SimpleSAML_Logger::debug($line);
}
}
return $message;
}
\saml\simplesamlphp-1.13.2\vendor\simplesamlphp\saml2\src\SAML2\EncryptedAssertion.php
public function getAssertion(XMLSecurityKey $inputKey, array $blacklist = array()) {
$assertionXML = SAML2_Utils::decryptElement($this->encryptedData, $inputKey, $blacklist);
$str = \SimpleSAML\Utils\XML::debugSAMLMessage($assertionXML, 'decrypt');
$sessionObject = SimpleSAML_Session::getSession();
$sessionObject->setData('string', 'SAMLAssertionCLEAR', $str);
return new SAML2_Assertion($assertionXML);
}
Hi @jaimeperez
You mention that it would be better to obtain the assertion instead of the response. Is this possible in the current version of simplesamlphp without changing any code? I'm trying to obtain this assertion so I can get an OAuth token from an OAuth 2.0 service, but I can't seem to get to the assertion without actually changing the simplesamlphp code?
Kind regards Nils
Hi @nlamot.
Yes, capturing the response doesn't make any sense since it's just the outer wrapper, the protocol message, not the piece of information carrying assertions about the user's identity. That would be the assertion, of course.
In any case, no, I'm afraid it's not possible.
@Desseres, I'm afraid your code is wrong. First, because you don't need to use \SimpleSAML\Utils\XML::debugSAMLMessage()
to retrieve the assertion, you already have it. Second, because if you want to save it, you shouldn't save it formatted, you should worry about storage space, not about formatting XML that's not intended to be shown to a user. And third, because you are taking the wrong approach to saving it in the session. The type
parameter in SimpleSAML_Session::setData
is not a data type (as in a string, or an integer), but a unique string that helps you identify the contents (as in SAMLAssertion). In any case, you shouldn't be storing the assertion like that. The assertion is something you get from a SAML auth source, so it should be the SAML auth source the one who dumps the assertion into its own state and marks it as persistent so that it is kept in the session.
Thanks @jaimeperez for your quick response! But in that case, your webserver cannot authenticate to webservices using the credentials of the logged in user? I need to consume an OData service using the credentials of my logged in user. Since I don't have his password and I cannot access his assertion, I can't find a way to do this.
I'm searching on this issue for months, but can't seem to find a solution. Is there something obvious I'm overlooking? This doesn't seem like a very exotic scenario to me?
Hi again!
Well, it's not an exotic scenario to OAuth, but this is not SimpleOAuthPHP, but SimpleSAMLphp 😄
In SAML, the IdP sends the SP a SAML response containing an assertion. SimpleSAMLphp (when acting as an SP) will process that and set up a session. You can then ask SSP in your application if the user is authenticated and what are his/her attributes. That's the normal use case in SAML, so if you compare to that, what you are trying to do is indeed exotic.
Woops, I didn't give you the complete background, sorry. :)
OAuth is our second try at it. First we also authenticated the webservice with SimpleSAML. The IdP is an AD-server. I read that you can use an "ActAs" from AD to implement this, but again we needed the SAML assertion to do this, since we don't have the user credentials.
Thank you very much for your feedback so far, it's nice to get confirmation that this indeed isn't possible and I'm not overlooking anything obvious.
Hi!
You should indeed never receive the user credentials 😉
I don't know if it is common to use SAML assertions as tokens for OAuth, to be honest. In any case, I don't have anything against making assertions available so that you can use them for that or any other use you might imagine, no matter how perverted that'd be 😆 My only requirement is to do it right, and of course PRs are always welcome!
Ok, thanks!
I certainly want to try and add this, but can you hint me where I could start looking for a place to do it?
Hi @nlamot!
It really depends on what you need. If you are fine with having a SAML2\Assertion
object that you can manipulate (and convert to XML if you want), then it's relatively simple, and could probably be done entirely in the AssertionConsumerService
endpoint (modules/saml/www/sp/saml2-acs.php). You basically need to capture the assertion (note the array iterating over a list of assertions, though I would say only the last one is used) and keep it in the state array, under a meaningful key, i.e.:
$state['saml:sp:Assertion'] = $assertion;
Then you would need to add that entry in the state array holding the assertion (remember this is an object, not the raw string containing the XML representation of the assertion) as persistent. Since the size of the assertion can be quite significant and this will end up stored in the session, I think it's mandatory to have a configuration option to enable this (disabled by default). E.g.:
$sp_c = $source->getMetadata();
if ($sp_c->getBoolean('KeepAssertions', false)) {
$state['PersistentAuthData'][] = 'saml:sp:Assertion';
}
Some additional considerations:
Audience
is SimpleSAMLphp, and if you are going to forward the assertion it doesn't make much sense that the receiving party would check the signature of an assertion targeted at someone else. If you really need the assertion to be signed, it would probably be better if you rebuild it (basically change the Audience
, the NameID
elements you may have, the Issuer
, and in general whatever fields are specific to the IdP or your SP), and then sign it with your own SP key. The trust relationship involving third-parties not running SAML should be established with your SP acting as a protocol translation proxy, not directly with the IdP.AttributeValue
elements containing anything than a string or a number will be broken and you won't get them. The eduPersonTargetedID
attribute is the best example of this, as it should contain a NameID
XML element (such as the one in the Subject
). If you really need to use that attribute (which you should modify in any case), a good way to do that would be an authentication processing filter that looks for it, and if found (and the value is a DOMNodeList
, as it is today), try to create a SAML2\XML\saml\NameID
object which is serializable and could be kept in the session. Anything that's not serializable (as DOM*
objects) will disappear as soon as you save it into the session.SAML2\Response::__construct()
to rebuild the XML from the DOMElement
object containing the assertion, and then keep it in a private property and modify the interface to allow fetching that property afterwards. The alternative to rebuilding the XML from the DOM would be to do this directly in the bindings, which is something I'd rather not do.I don't know if I missed anything. As you can see, it's relatively simple, but not that simple anyway... 😄
Hello all, I'm just pitching in to say that I have the exact same requirement. It comes from using WSO2, check out this instructions page: https://docs.wso2.com/display/AM200/Exchanging+SAML2+Bearer+Tokens+with+OAuth2+-+SAML+Extension+Grant+Type
@alexsenatore could you by any chance share your SSP patch? I'd like to see this working, even if I have to change my implentation later...
BTW, I asked the question here: http://stackoverflow.com/questions/40723999/how-to-retrieve-saml2-token-in-php-for-getting-an-oauth-token-from-wso2-apim
I see this is still open. I used a different package to do SP but used simplesamlphp as IDP.
Here is sample code (using onelogin/php-saml as SP):
if (isset($_POST['SAMLResponse']))
{
$samlSettings = new OneLogin_Saml2_Settings();
$samlResponse = new OneLogin_Saml2_Response($samlSettings, $_POST['SAMLResponse']);
$dom = $samlResponse->getXMLDocument();
$xml = simplexml_import_dom($dom);
$parse = $xml->xpath('saml:Assertion');
$parsexml = new SimpleXmlElement($parse[0]->asXML());
$parsexml->addAttribute('xmlns:xmlns:saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
$samlAssertion = base64_encode($parsexml->asXML());
}
Pass the saml assertion to WSO2 to get Token.
Hi all, considering the following scenario
In this scenario, IdP and Backend Service Platform are trusted and OAuth2 token is generated using SAMLBearer grant type by passing also the assertion included in the response.
The E2E flow works in this way 1) user hits a Web App URL 2) user is redirected to IdP (SP initiated flow) 3) user is authenticated on IdP and redirected to Web Application 4) web application retrieves SAML assertion from the SAML response 5) web application requests an OAuth2 token by using SAMLBearer as grant type and the assertion as value 6) Backend Service Platform checks the SAML Response (signature, encryption etc...) and generates the token 7) web application starts consuming APIs
We were able to succesfully implement the first three steps using simpleSAMLphp v1.13 However, there are no APIs available to get the assertion from the response for step 4 (the getAttributes can only be used to retrieve claims..)
In order to implement the E2E flow, we had to add a method in the following file to retrieve the assertion • simplesamlphp\vendor\simplesamlphp\saml2\src\SAML2\HTTPPost.php
The assertion is then retireved in • simplesamlphp\modulerà\saml\www\sp\saml2-acs.php
With this change we were able to complete the flow
I think it make sense to have this as an enhancement, so that the same scenario can be supported out of the box.