diff --git a/README.md b/README.md index 12289672..7e0b80e2 100644 --- a/README.md +++ b/README.md @@ -675,6 +675,17 @@ header('Location: ' . $ssoBuiltUrl); exit(); ``` +If a check on the future SAMLResponse IssueInstant and the AuthNRequest IssueInstant to be sent is required, that AuthNRequest IssueInstant must to be extracted and saved. + +```php +$ssoBuiltUrl = $auth->login(null, array(), false, false, true); +$_SESSION['AuthNRequestIssueInstant'] = $auth->getLastRequestIssueInstant(); +header('Pragma: no-cache'); +header('Cache-Control: no-cache, must-revalidate'); +header('Location: ' . $ssoBuiltUrl); +exit(); +```` + #### The SP Endpoints #### Related to the SP there are three important views: The metadata view, the ACS view and the SLS view. The toolkit @@ -743,8 +754,15 @@ if (isset($_SESSION) && isset($_SESSION['AuthNRequestID'])) { $requestID = null; } -$auth->processResponse($requestID); +if (isset($_SESSION) && isset($_SESSION['AuthNRequestIssueInstant'])) { + $requestIssueInstant = $_SESSION['AuthNRequestIssueInstant']; +} else { + $requestIssueInstant = null; +} + +$auth->processResponse($requestID, $requestIssueInstant); unset($_SESSION['AuthNRequestID']); +unset($_SESSION['AuthNRequestIssueInstant']); $errors = $auth->getErrors(); @@ -884,8 +902,13 @@ if (isset($_SESSION) && isset($_SESSION['LogoutRequestID'])) { } else { $requestID = null; } +if (isset($_SESSION) && isset($_SESSION['LogoutRequestIssueInstant'])) { + $requestIssueInstant = $_SESSION['LogoutRequestIssueInstant']; +} else { + $requestIssueInstant = null; +} -$auth->processSLO(false, $requestID); +$auth->processSLO(false, $requestID, false, null, false, $requestIssueInstant); $errors = $auth->getErrors(); @@ -1058,6 +1081,17 @@ header('Location: ' . $sloBuiltUrl); exit(); ``` +If a check on the future LogoutResponse IssueInstant and the LogoutRequest IssueInstant to be sent is required, that LogoutRequest IssueInstant must to be extracted and saved. + +```php +$sloBuiltUrl = $auth->logout(null, $paramters, $nameId, $sessionIndex, true); +$_SESSION['LogoutRequestIssueInstant'] = $auth->getLastRequestIssueInstant(); +header('Pragma: no-cache'); +header('Cache-Control: no-cache, must-revalidate'); +header('Location: ' . $sloBuiltUrl); +exit(); +```` + #### Example of a view that initiates the SSO request and handles the response (is the acs target) #### We can code a unique file that initiates the SSO process, handle the response, get the attributes, initiate @@ -1310,12 +1344,12 @@ Main class of OneLogin PHP Toolkit * `getErrors` - Returns if there were any error * `getSSOurl` - Gets the SSO url. * `getSLOurl` - Gets the SLO url. - * `getLastRequestID` - The ID of the last Request SAML message generated. * `buildRequestSignature` - Generates the Signature for a SAML Request * `buildResponseSignature` - Generates the Signature for a SAML Response * `getSettings` - Returns the settings info * `setStrict` - Set the strict mode active/disable * `getLastRequestID` - Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider. + * `getLastRequestIssueInstant` - Gets the IssueInstant attribute of the last AuthNRequest or LogoutRequest generated by the Service Provider. * `getLastRequestXML` - Returns the most recently-constructed/processed XML SAML request (AuthNRequest, LogoutRequest) * `getLastResponseXML` - Returns the most recently-constructed/processed XML SAML response (SAMLResponse, LogoutResponse). If the SAMLResponse had an encrypted assertion, decrypts it. diff --git a/demo1/index.php b/demo1/index.php index 2400451f..ac93f58b 100644 --- a/demo1/index.php +++ b/demo1/index.php @@ -22,6 +22,14 @@ # header('Location: ' . $ssoBuiltUrl); # exit(); + # If AuthNRequest IssueInstant need to be saved in order to later validate it, do instead + # $ssoBuiltUrl = $auth->login(null, array(), false, false, true); + # $_SESSION['AuthNRequestIssueInstant'] = $auth->getLastRequestIssueInstant(); + # header('Pragma: no-cache'); + # header('Cache-Control: no-cache, must-revalidate'); + # header('Location: ' . $ssoBuiltUrl); + # exit(); + } else if (isset($_GET['sso2'])) { $returnTo = $spBaseUrl.'/demo1/attrs.php'; $auth->login($returnTo); @@ -58,6 +66,14 @@ # header('Location: ' . $sloBuiltUrl); # exit(); + # If LogoutRequest IssueInstant need to be saved in order to later validate it, do instead + # $sloBuiltUrl = $auth->logout(null, $paramters, $nameId, $sessionIndex, true); + # $_SESSION['LogoutRequestIssueInstant'] = $auth->getLastRequestIssueInstant(); + # header('Pragma: no-cache'); + # header('Cache-Control: no-cache, must-revalidate'); + # header('Location: ' . $sloBuiltUrl); + # exit(); + } else if (isset($_GET['acs'])) { if (isset($_SESSION) && isset($_SESSION['AuthNRequestID'])) { $requestID = $_SESSION['AuthNRequestID']; @@ -65,7 +81,13 @@ $requestID = null; } - $auth->processResponse($requestID); + if (isset($_SESSION) && isset($_SESSION['AuthNRequestIssueInstant'])) { + $requestIssueInstant = $_SESSION['AuthNRequestIssueInstant']; + } else { + $requestIssueInstant = null; + } + + $auth->processResponse($requestID, $requestIssueInstant); $errors = $auth->getErrors(); @@ -95,7 +117,13 @@ $requestID = null; } - $auth->processSLO(false, $requestID); + if (isset($_SESSION) && isset($_SESSION['LogoutRequestIssueInstant'])) { + $requestIssueInstant = $_SESSION['LogoutRequestIssueInstant']; + } else { + $requestIssueInstant = null; + } + + $auth->processSLO(false, $requestID, false, null, false, $requestIssueInstant); $errors = $auth->getErrors(); if (empty($errors)) { echo '
Sucessfully logged out
'; diff --git a/lib/Saml2/Auth.php b/lib/Saml2/Auth.php index 591ffd37..4c2f2de6 100644 --- a/lib/Saml2/Auth.php +++ b/lib/Saml2/Auth.php @@ -123,6 +123,13 @@ class OneLogin_Saml2_Auth */ private $_lastRequestID; + /** + * Last AuthNRequest or LogoutRequest IssueInstant generated by this Service Provider + * + * @var string + */ + private $_lastRequestIssueInstant; + /** * The most recently-constructed/processed XML SAML request * (AuthNRequest, LogoutRequest) @@ -189,7 +196,7 @@ public function setStrict($value) * @throws OneLogin_Saml2_Error * @throws OneLogin_Saml2_ValidationError */ - public function processResponse($requestId = null) + public function processResponse($requestId = null, $requestIssueInstant = null) { $this->_errors = array(); $this->_errorReason = null; @@ -198,7 +205,7 @@ public function processResponse($requestId = null) $response = new OneLogin_Saml2_Response($this->_settings, $_POST['SAMLResponse']); $this->_lastResponse = $response->getXMLDocument(); - if ($response->isValid($requestId)) { + if ($response->isValid($requestId, $requestIssueInstant)) { $this->_attributes = $response->getAttributes(); $this->_attributesWithFriendlyName = $response->getAttributesWithFriendlyName(); $this->_nameid = $response->getNameId(); @@ -237,14 +244,14 @@ public function processResponse($requestId = null) * * @throws OneLogin_Saml2_Error */ - public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay = false) + public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay = false, $requestIssueInstant = null) { $this->_errors = array(); $this->_errorReason = null; if (isset($_GET['SAMLResponse'])) { $logoutResponse = new OneLogin_Saml2_LogoutResponse($this->_settings, $_GET['SAMLResponse']); $this->_lastResponse = $logoutResponse->getXML(); - if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer)) { + if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer, $requestIssueInstant)) { $this->_errors[] = 'invalid_logout_response'; $this->_errorReason = $logoutResponse->getError(); } else if ($logoutResponse->getStatus() !== OneLogin_Saml2_Constants::STATUS_SUCCESS) { @@ -497,6 +504,7 @@ public function login($returnTo = null, $parameters = array(), $forceAuthn = fal $this->_lastRequest = $authnRequest->getXML(); $this->_lastRequestID = $authnRequest->getId(); + $this->_lastRequestIssueInstant = $authnRequest->getIssueInstant(); $samlRequest = $authnRequest->getRequest(); $parameters['SAMLRequest'] = $samlRequest; @@ -554,6 +562,7 @@ public function logout($returnTo = null, $parameters = array(), $nameId = null, $this->_lastRequest = $logoutRequest->getXML(); $this->_lastRequestID = $logoutRequest->id; + $this->_lastRequestIssueInstant = $logoutRequest->getIssueInstant(); $samlRequest = $logoutRequest->getRequest(); @@ -624,6 +633,16 @@ public function getLastRequestID() return $this->_lastRequestID; } + /** + * Gets the IssueInstant of the last AuthNRequest or LogoutRequest generated by the Service Provider. + * + * @return string The IssueInstant of the Request SAML message. + */ + public function getLastRequestIssueInstant() + { + return $this->_lastRequestIssueInstant; + } + /** * Generates the Signature for a SAML Request * diff --git a/tests/src/OneLogin/Saml2/AuthTest.php b/tests/src/OneLogin/Saml2/AuthTest.php index 94e3872f..d9f464ef 100644 --- a/tests/src/OneLogin/Saml2/AuthTest.php +++ b/tests/src/OneLogin/Saml2/AuthTest.php @@ -57,6 +57,22 @@ public function testGetLastRequestID() $this->assertNotEquals($id1, $id2); } + /** + * Tests the getLastRequestIssueInstant method of the OneLogin_Saml2_Auth class + * + * @covers OneLogin_Saml2_Auth::getLastRequestIssueInstant + */ + public function testGetLastRequestIssueInstant() + { + $targetSSOURL = $this->_auth->login(null, array(), false, false, true, false, false); + $issueInstant1 = $this->_auth->getLastRequestIssueInstant(); + $this->assertNotNull($issueInstant1); + + $targetSLOURL = $this->_auth->logout(null, array(), null, null, true, null, null); + $issueInstant2 = $this->_auth->getLastRequestIssueInstant(); + $this->assertNotNull($issueInstant2); + } + /** * Tests the getSSOurl method of the OneLogin_Saml2_Auth class *