diff --git a/docs/simplesamlphp-idp.txt b/docs/simplesamlphp-idp.txt index e68824a68a153f08e0e5c2c801ab28f89f7e49af..661705d45e707cc94cc1f76d59b00b579590bfe9 100644 --- a/docs/simplesamlphp-idp.txt +++ b/docs/simplesamlphp-idp.txt @@ -689,6 +689,8 @@ redirect.validate + + Configuring metadata for a Shibboleth 1.3 IdP --------------------------------------------- @@ -717,6 +719,9 @@ used as the scope. */ 'scopedattributes' => array('eduPersonPrincipalName'), + + + Test IdP -------- @@ -752,7 +757,19 @@ improvements or contribute with code or plugins of your own. [Visit and contribute to the simpleSAMLphp wiki](https://ow.feide.no/simplesamlphp:start) -A. Writing your own authentication module + +A. IdP-first setup +------------------ + +If you do not want to start the SSO flow at the SP, you may use the IdP-first setup. To do this, redirect the user to the SSOService endpoint on the IdP with one parameter `spentityid` that match the SP EntityId that the user should be logged into. + +Here is an example of such an url: + + https://sp.example.org/simplesaml/saml2/idp/SSOService.php?spentityid=dev.andreas.feide.no + + + +B. Writing your own authentication module ---------------------------------------------- You can write your own authentication module. Just copy one of the diff --git a/docs/simplesamlphp-sp.txt b/docs/simplesamlphp-sp.txt index 5434372d3ac8184cfa81cdaad092489c4f451ac6..c6f998259c5e8e039e41ad047f46910da7cdbf1c 100644 --- a/docs/simplesamlphp-sp.txt +++ b/docs/simplesamlphp-sp.txt @@ -165,6 +165,9 @@ idpdisco.url one is also unset, the builtin default discovery service will be used. +RelayState +: This may be a relative or absolute URL on the Service Provider that the user should be redirected to after successful authentication. This parameter MUST be used if you are using an IdP-first setup with SAML 2.0, where no AuthNRequest is sent. + privatekey : File name of private key to be used for signing messages. diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php index ffb55787a06020910f28ea2e962acc17da8c988a..23e6bcc93d1ccf4056ec37b7ddc3bf2f3d0de715 100644 --- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php +++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php @@ -243,8 +243,6 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { SimpleSAML_Utilities::redirect($redirectURL); } - - } diff --git a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php index 72cf1cb2fdb83c644fa0091597ccf9633e66597f..a9245352b11f2b57229524b4997d71b964e2d6cc 100644 --- a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php +++ b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php @@ -718,8 +718,9 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { $nameid = $this->generateNameID($nameidformat, $nameIdValue, $spnamequalifier); } - - + $inresponsetoText = ''; + if (!empty($inresponseto)) $inresponsetoText = 'InResponseTo="' . htmlspecialchars($inresponseto). '" '; + $assertion = ""; if ($status === 'Success') { $assertion = '<saml:Assertion Version="2.0" @@ -729,7 +730,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ' . $nameid . ' <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData NotOnOrAfter="' . $assertionExpire . '" - InResponseTo="' . htmlspecialchars($inresponseto). '" + ' . $inresponsetoText . ' Recipient="' . htmlspecialchars($destination) . '"/> </saml:SubjectConfirmation> </saml:Subject> @@ -763,7 +764,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="' . $id . '" - InResponseTo="' . htmlspecialchars($inresponseto) . '" Version="2.0" + ' . $inresponsetoText . ' Version="2.0" IssueInstant="' . $issueInstant . '" Destination="' . htmlspecialchars($destination) . '"> <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . htmlspecialchars($issuer) . '</saml:Issuer> diff --git a/www/saml2/idp/SSOService.php b/www/saml2/idp/SSOService.php index 191d892d8e786b546683c8d2c5ff9f6a926b03e7..e6865f88225764dded261655688ec30d14cf5b8c 100644 --- a/www/saml2/idp/SSOService.php +++ b/www/saml2/idp/SSOService.php @@ -34,6 +34,11 @@ if (!$config->getValue('enable.saml20-idp', false)) SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS'); +/* + * Initiate some variables + */ +$isPassive = FALSE; + /* * If the SAMLRequest query parameter is set, we got an incoming Authentication Request * at this interface. @@ -145,6 +150,19 @@ if (isset($_GET['SAMLRequest'])) { $authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId); $requestcache = $authProcState['core:saml20-idp:requestcache']; +/** + * If the spentityid parameter is provided, we will fallback to a unsolited response to the SP. + */ +} elseif(array_key_exists('spentityid', $_GET)) { + + /* Creating a request cache, even though there was no request, and adding the + * information that is neccessary to be able to respond with an unsolited response + */ + $requestcache = array( + 'Issuer' => $_GET['spentityid'], + ); + + } else { SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS'); } @@ -322,14 +340,17 @@ if($needAuth && !$isPassive) { // Right now the list is used for SAML 2.0 only. $session->add_sp_session($spentityid); + $requestID = NULL; $relayState = NULL; + if (array_key_exists('RequestID', $requestcache)) $requestID = $requestcache['RequestID']; + if (array_key_exists('RelayState', $requestcache)) $relayState = $requestcache['RelayState']; // Generate an SAML 2.0 AuthNResponse message $ar = new SimpleSAML_XML_SAML20_AuthnResponse($config, $metadata); - $authnResponseXML = $ar->generate($idpentityid, $spentityid, $requestcache['RequestID'], null, $attributes); + $authnResponseXML = $ar->generate($idpentityid, $spentityid, $requestID, null, $attributes); // Sending the AuthNResponse using HTTP-Post SAML 2.0 binding $httppost = new SimpleSAML_Bindings_SAML20_HTTPPost($config, $metadata); - $httppost->sendResponse($authnResponseXML, $idmetaindex, $spentityid, $requestcache['RelayState']); + $httppost->sendResponse($authnResponseXML, $idmetaindex, $spentityid, $relayState); } catch(Exception $exception) { SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATEAUTHNRESPONSE', $exception); diff --git a/www/saml2/sp/AssertionConsumerService.php b/www/saml2/sp/AssertionConsumerService.php index 30026d8f7cf17ec813ef753c98e7aedc77cc548c..ba3cf812f01c2a1c9f4f27c0467bf288750c0865 100644 --- a/www/saml2/sp/AssertionConsumerService.php +++ b/www/saml2/sp/AssertionConsumerService.php @@ -70,7 +70,8 @@ if (empty($_POST['SAMLResponse'])) try { $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - + $spmetadata = $metadata->getMetaDataCurrent(); + $binding = new SimpleSAML_Bindings_SAML20_HTTPPost($config, $metadata); $authnResponse = $binding->decodeResponse($_POST); @@ -83,7 +84,12 @@ try { /* Fall back to RelayState. */ $info = array(); $info['RelayState'] = $authnResponse->getRelayState(); - if(!isset($info['RelayState'])) { + if(empty($info['RelayState'])) { + if (array_key_exists('RelayState', $spmetadata)) { + $info['RelayState'] = $spmetadata['RelayState']; + } + } + if(empty($info['RelayState'])) { /* RelayState missing. */ SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NORELAYSTATE'); } @@ -111,7 +117,7 @@ try { $idpentityid = $authnResponse->getIssuer(); $idpmetadata = $metadata->getMetaData($idpentityid, 'saml20-idp-remote'); - $spmetadata = $metadata->getMetaDataCurrent(); + /*