diff --git a/src/lti/LTI_Message_Launch.php b/src/lti/LTI_Message_Launch.php index 7b45bb8..30b2f99 100644 --- a/src/lti/LTI_Message_Launch.php +++ b/src/lti/LTI_Message_Launch.php @@ -73,18 +73,19 @@ public static function from_cache($launch_id, Database $database, Cache $cache = * Validates all aspects of an incoming LTI message launch and caches the launch if successful. * * @param array|string $request An array of post request parameters. If not set will default to $_POST. + * @param boolean $insecurelyBypassStateValidation If true, the caller is responsible for securing against CSRF. * * @throws LTI_Exception Will throw an LTI_Exception if validation fails. * @return LTI_Message_Launch Will return $this if validation is successful. */ - public function validate(array $request = null) { + public function validate(array $request = null, $insecurelyBypassStateValidation = false) { if ($request === null) { $request = $_POST; } $this->request = $request; - return $this->validate_state() + return $this->validate_state($insecurelyBypassStateValidation) ->validate_jwt_format() ->validate_nonce() ->validate_registration() @@ -242,7 +243,13 @@ private function cache_launch_data() { return $this; } - private function validate_state() { + /** + * @param boolean $insecurelyBypassStateValidation If true, the caller is responsible for securing against CSRF. + */ + private function validate_state($insecurelyBypassStateValidation = false) { + if ($insecurelyBypassStateValidation) { + return $this; + } // Check State for OIDC. $expectedState = $this->cookie->get_cookie('lti1p3_' . $this->request['state']); if (empty($expectedState) || $expectedState !== $this->request['state']) { diff --git a/tests/unit/LTI_Message_Launch_Test.php b/tests/unit/LTI_Message_Launch_Test.php index 2be4180..f3e94c4 100644 --- a/tests/unit/LTI_Message_Launch_Test.php +++ b/tests/unit/LTI_Message_Launch_Test.php @@ -51,6 +51,23 @@ public function testValidateStateWithInvalidStateThrowsException() )->validate(); } + public function testValidateStateWithInvalidStateThrowsNoStateNotFoundExceptionIfUserBypassesStateValidation() + { + $this->setExpectedException(LTI_JWT_Exception::class, 'Missing id_token'); + /** @var Cookie|\PHPUnit_Framework_MockObject_MockObject $cookie */ + $cookie = $this->getMockBuilder(Cookie::class) + ->setMethods(['get_cookie']) + ->getMock(); + + $cookie->expects($this->never())->method('get_cookie'); + + LTI_Message_Launch::newInstance( + new DummyDatabase(), + null, + $cookie + )->validate(null, true); + } + public function testValidateState() { $state = uniqid();