diff --git a/modules/saml2/lib/Auth/Source/SP.php b/modules/saml2/lib/Auth/Source/SP.php index 6dc6ce7faf1166f94fc93d86d781a67bc8559e9c..3af1c5d022880d142b2240f3335d3e07b3a2bc5d 100644 --- a/modules/saml2/lib/Auth/Source/SP.php +++ b/modules/saml2/lib/Auth/Source/SP.php @@ -15,10 +15,17 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source { /** - * The string used to identify our states. + * The identifier for the stage where we have sent a discovery service request. + */ + const STAGE_DISCO = 'saml2:SP-DiscoSent'; + + + /** + * The identifier for the stage where we have sent a SSO request. */ const STAGE_SENT = 'saml2:SP-SSOSent'; + /** * The string used to identify our logout state. */ @@ -82,7 +89,7 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source { if (array_key_exists('idp', $config)) { $this->idp = $config['idp']; } else { - throw new Exception('TODO: Add support for IdP discovery.'); + $this->idp = NULL; } } @@ -100,17 +107,65 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source { /* We are going to need the authId in order to retrieve this authentication source later. */ $state[self::AUTHID] = $this->authId; + if ($this->idp === NULL) { + $this->initDisco($state); + } + + $this->initSSO($this->idp, $state); + } + + + /** + * Send authentication request to specified IdP. + * + * @param string $idp The IdP we should send the request to. + * @param array $state Our state array. + */ + public function initDisco($state) { + assert('is_array($state)'); + + $id = SimpleSAML_Auth_State::saveState($state, self::STAGE_DISCO); + + $config = SimpleSAML_Configuration::getInstance(); + + $discoURL = $config->getString('idpdisco.url.saml20', NULL); + if ($discoURL === NULL) { + /* Fallback to internal discovery service. */ + $discoURL = SimpleSAML_Module::getModuleURL('saml2/disco.php'); + } + + $returnTo = SimpleSAML_Module::getModuleURL('saml2/sp/discoresp.php'); + $returnTo = SimpleSAML_Utilities::addURLparameter($returnTo, array('AuthID' => $id)); + + SimpleSAML_Utilities::redirect($discoURL, array( + 'entityID' => $this->entityId, + 'return' => $returnTo, + 'returnIDParam' => 'idpentityid') + ); + } + + + /** + * Send authentication request to specified IdP. + * + * @param string $idp The IdP we should send the request to. + * @param array $state Our state array. + */ + public function initSSO($idp, $state) { + assert('is_string($idp)'); + assert('is_array($state)'); + $id = SimpleSAML_Auth_State::saveState($state, self::STAGE_SENT); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - $idpMetadata = $metadata->getMetaData($this->idp, 'saml20-idp-remote'); + $idpMetadata = $metadata->getMetaData($idp, 'saml20-idp-remote'); $config = SimpleSAML_Configuration::getInstance(); $sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata); $req = $sr->generate($this->entityId, $idpMetadata['SingleSignOnService']); $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - $httpredirect->sendMessage($req, $this->entityId, $this->idp, $id); + $httpredirect->sendMessage($req, $this->entityId, $idp, $id); exit(0); } diff --git a/modules/saml2/www/disco.php b/modules/saml2/www/disco.php new file mode 100644 index 0000000000000000000000000000000000000000..f3e526c5ab2a83d4d23b78c89f477b01d5beabbd --- /dev/null +++ b/modules/saml2/www/disco.php @@ -0,0 +1,10 @@ +<?php + +/** + * Builtin IdP discovery service. + */ + +$discoHandler = new SimpleSAML_XHTML_IdPDisco('saml20'); +$discoHandler->handleRequest(); + +?> \ No newline at end of file diff --git a/modules/saml2/www/sp/discoresp.php b/modules/saml2/www/sp/discoresp.php new file mode 100644 index 0000000000000000000000000000000000000000..0ce9dd907602eb64c6bba5bf89d5dc6f55904c71 --- /dev/null +++ b/modules/saml2/www/sp/discoresp.php @@ -0,0 +1,28 @@ +<?php + +/** + * Handler for response from IdP discovery service. + */ + +if (!array_key_exists('AuthID', $_REQUEST)) { + throw new SimpleSAML_Error_BadRequest('Missing AuthID to discovery service response handler'); +} + +if (!array_key_exists('idpentityid', $_REQUEST)) { + throw new SimpleSAML_Error_BadRequest('Missing idpentityid to discovery service response handler'); +} + +$state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthID'], sspmod_saml2_Auth_Source_SP::STAGE_DISCO); + +/* Find authentication source. */ +assert('array_key_exists(sspmod_saml2_Auth_Source_SP::AUTHID, $state)'); +$sourceId = $state[sspmod_saml2_Auth_Source_SP::AUTHID]; + +$source = SimpleSAML_Auth_Source::getById($sourceId); +if ($source === NULL) { + throw new Exception('Could not find authentication source with id ' . $sourceId); +} + +$source->initSSO($_REQUEST['idpentityid'], $state); + +?> \ No newline at end of file