Skip to content

Commit df2a40c

Browse files
committed
Add ForceAuh and IsPassive support. Requested in SAML-Toolkits#49
1 parent 7e70d82 commit df2a40c

File tree

7 files changed

+266
-14
lines changed

7 files changed

+266
-14
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,12 @@ $auth = new OneLogin_Saml2_Auth();
513513
$auth->login($newTargetUrl);
514514
```
515515

516+
The login method can recieve 3 more optional parameters:
517+
518+
* $parameters An array of parameters that will be added to the GET in the HTTP-Redirect.
519+
* $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
520+
* $isPassive When true the AuthNReuqest will set the Ispassive='true'
521+
516522
#### The SP Endpoints ####
517523

518524
Related to the SP there are 3 important views: The metadata view, the ACS view and the SLS view. The toolkit

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "onelogin/php-saml",
33
"description": "OneLogin PHP SAML Toolkit",
44
"license": "MIT",
5-
"version": "2.1.0",
5+
"version": "2.3.0",
66
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
77
"keywords": ["saml", "saml2", "onelogin"],
88
"autoload": {
@@ -31,6 +31,6 @@
3131
"suggest": {
3232
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)",
3333
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
34-
"ext-gettext": "Install gettext and php5-gettext libs to handle translations"
34+
"ext-gettext": "Install gettext and php5-gettext libs to handle translations"
3535
}
3636
}

lib/Saml2/Auth.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public function processResponse($requestId = null)
125125
* @param boolean $keepLocalSession When false will destroy the local session, otherwise will keep it
126126
* @param string $requestId The ID of the LogoutRequest sent by this SP to the IdP
127127
*/
128-
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer=false)
128+
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false)
129129
{
130130
$this->_errors = array();
131131
if (isset($_GET) && isset($_GET['SAMLResponse'])) {
@@ -280,11 +280,11 @@ public function getAttribute($name)
280280
* @param string $returnTo The target URL the user should be returned to after login.
281281
* @param array $parameters Extra parameters to be added to the GET
282282
*/
283-
public function login($returnTo = null, $parameters = array())
283+
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false)
284284
{
285285
assert('is_array($parameters)');
286286

287-
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings);
287+
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive);
288288

289289
$samlRequest = $authnRequest->getRequest();
290290
$parameters['SAMLRequest'] = $samlRequest;

lib/Saml2/AuthnRequest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class OneLogin_Saml2_AuthnRequest
3030
*
3131
* @param OneLogin_Saml2_Settings $settings Settings
3232
*/
33-
public function __construct(OneLogin_Saml2_Settings $settings)
33+
public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false)
3434
{
3535
$this->_settings = $settings;
3636

@@ -62,6 +62,22 @@ public function __construct(OneLogin_Saml2_Settings $settings)
6262
}
6363
}
6464

65+
$forceAuthnStr = '';
66+
if ($forceAuthn) {
67+
$forceAuthnStr = <<<FORCEAUTHN
68+
69+
ForceAuthn="true"
70+
FORCEAUTHN;
71+
}
72+
73+
$isPassiveStr = '';
74+
if ($isPassive) {
75+
$isPassiveStr = <<<ISPASSIVE
76+
77+
IsPassive="true"
78+
ISPASSIVE;
79+
}
80+
6581
$requestedAuthnStr = '';
6682
if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
6783
if ($security['requestedAuthnContext'] === true) {
@@ -85,7 +101,7 @@ public function __construct(OneLogin_Saml2_Settings $settings)
85101
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
86102
ID="$id"
87103
Version="2.0"
88-
{$providerNameStr}
104+
{$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
89105
IssueInstant="$issueInstant"
90106
Destination="{$idpData['singleSignOnService']['url']}"
91107
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"

lib/Saml2/version.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"php-saml": {
3-
"version": "2.2.0",
4-
"released": "01/10/2014"
3+
"version": "2.3.0",
4+
"released": "13/01/2015"
55
}
66
}

tests/src/OneLogin/Saml2/AuthTest.php

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,168 @@ public function testLoginSigned()
746746
}
747747
}
748748

749+
/**
750+
* Tests the login method of the OneLogin_Saml2_Auth class
751+
* Case Logout with no parameters. A AuthN Request is built with ForceAuthn and redirect executed
752+
*
753+
* @covers OneLogin_Saml2_Auth::login
754+
* @runInSeparateProcess
755+
*/
756+
public function testLoginForceAuthN()
757+
{
758+
$settingsDir = TEST_ROOT .'/settings/';
759+
include $settingsDir.'settings1.php';
760+
761+
$settingsInfo['security']['authnRequestsSigned'] = true;
762+
763+
$auth = new OneLogin_Saml2_Auth($settingsInfo);
764+
765+
try {
766+
// The Header of the redirect produces an Exception
767+
$returnTo = 'http://example.com/returnto';
768+
$auth->login($returnTo);
769+
// Do not ever get here
770+
$this->assertFalse(true);
771+
} catch (Exception $e) {
772+
$this->assertContains('Cannot modify header information', $e->getMessage());
773+
$trace = $e->getTrace();
774+
$targetUrl = getUrlFromRedirect($trace);
775+
$parsedQuery = getParamsFromUrl($targetUrl);
776+
777+
$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
778+
$this->assertContains($ssoUrl, $targetUrl);
779+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
780+
$encodedRequest = $parsedQuery['SAMLRequest'];
781+
$decoded = base64_decode($encodedRequest);
782+
$request = gzinflate($decoded);
783+
$this->assertNotContains('ForceAuthn="true"', $request);
784+
}
785+
786+
try {
787+
// The Header of the redirect produces an Exception
788+
$returnTo = 'http://example.com/returnto';
789+
790+
$auth->login($returnTo, array(), false, false);
791+
// Do not ever get here
792+
$this->assertFalse(true);
793+
} catch (Exception $e) {
794+
$this->assertContains('Cannot modify header information', $e->getMessage());
795+
$trace2 = $e->getTrace();
796+
$targetUrl2 = getUrlFromRedirect($trace2);
797+
$parsedQuery2 = getParamsFromUrl($targetUrl2);
798+
799+
$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
800+
$this->assertContains($ssoUrl2, $targetUrl2);
801+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
802+
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
803+
$decoded2 = base64_decode($encodedRequest2);
804+
$request2 = gzinflate($decoded2);
805+
$this->assertNotContains('ForceAuthn="true"', $request2);
806+
}
807+
808+
try {
809+
// The Header of the redirect produces an Exception
810+
$returnTo = 'http://example.com/returnto';
811+
$auth->login($returnTo, array(), true, false);
812+
// Do not ever get here
813+
$this->assertFalse(true);
814+
} catch (Exception $e) {
815+
$this->assertContains('Cannot modify header information', $e->getMessage());
816+
$trace3 = $e->getTrace();
817+
$targetUrl3 = getUrlFromRedirect($trace3);
818+
$parsedQuery3 = getParamsFromUrl($targetUrl3);
819+
820+
$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
821+
$this->assertContains($ssoUrl3, $targetUrl3);
822+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
823+
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
824+
$decoded3 = base64_decode($encodedRequest3);
825+
$request3 = gzinflate($decoded3);
826+
$this->assertContains('ForceAuthn="true"', $request3);
827+
}
828+
829+
}
830+
831+
/**
832+
* Tests the login method of the OneLogin_Saml2_Auth class
833+
* Case Logout with no parameters. A AuthN Request is built with IsPassive and redirect executed
834+
*
835+
* @covers OneLogin_Saml2_Auth::login
836+
* @runInSeparateProcess
837+
*/
838+
public function testLoginIsPassive()
839+
{
840+
$settingsDir = TEST_ROOT .'/settings/';
841+
include $settingsDir.'settings1.php';
842+
843+
$settingsInfo['security']['authnRequestsSigned'] = true;
844+
845+
$auth = new OneLogin_Saml2_Auth($settingsInfo);
846+
847+
try {
848+
// The Header of the redirect produces an Exception
849+
$returnTo = 'http://example.com/returnto';
850+
$auth->login($returnTo);
851+
// Do not ever get here
852+
$this->assertFalse(true);
853+
} catch (Exception $e) {
854+
$this->assertContains('Cannot modify header information', $e->getMessage());
855+
$trace = $e->getTrace();
856+
$targetUrl = getUrlFromRedirect($trace);
857+
$parsedQuery = getParamsFromUrl($targetUrl);
858+
859+
$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
860+
$this->assertContains($ssoUrl, $targetUrl);
861+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
862+
$encodedRequest = $parsedQuery['SAMLRequest'];
863+
$decoded = base64_decode($encodedRequest);
864+
$request = gzinflate($decoded);
865+
$this->assertNotContains('IsPassive="true"', $request);
866+
}
867+
868+
try {
869+
// The Header of the redirect produces an Exception
870+
$returnTo = 'http://example.com/returnto';
871+
$auth->login($returnTo, array(), false, false);
872+
// Do not ever get here
873+
$this->assertFalse(true);
874+
} catch (Exception $e) {
875+
$this->assertContains('Cannot modify header information', $e->getMessage());
876+
$trace2 = $e->getTrace();
877+
$targetUrl2 = getUrlFromRedirect($trace2);
878+
$parsedQuery2 = getParamsFromUrl($targetUrl2);
879+
880+
$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
881+
$this->assertContains($ssoUrl2, $targetUrl2);
882+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
883+
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
884+
$decoded2 = base64_decode($encodedRequest2);
885+
$request2 = gzinflate($decoded2);
886+
$this->assertNotContains('IsPassive="true"', $request2);
887+
}
888+
889+
try {
890+
// The Header of the redirect produces an Exception
891+
$returnTo = 'http://example.com/returnto';
892+
$auth->login($returnTo, array(), false, true);
893+
// Do not ever get here
894+
$this->assertFalse(true);
895+
} catch (Exception $e) {
896+
$this->assertContains('Cannot modify header information', $e->getMessage());
897+
$trace3 = $e->getTrace();
898+
$targetUrl3 = getUrlFromRedirect($trace3);
899+
$parsedQuery3 = getParamsFromUrl($targetUrl3);
900+
901+
$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
902+
$this->assertContains($ssoUrl3, $targetUrl3);
903+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
904+
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
905+
$decoded3 = base64_decode($encodedRequest3);
906+
$request3 = gzinflate($decoded3);
907+
$this->assertContains('IsPassive="true"', $request3);
908+
}
909+
}
910+
749911
/**
750912
* Tests the logout method of the OneLogin_Saml2_Auth class
751913
* Case Logout with no parameters. A logout Request is built and redirect executed

tests/src/OneLogin/Saml2/AuthnRequestTest.php

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function setUp()
2727
*/
2828
public function testCreateDeflatedSAMLRequestURLParameter()
2929
{
30-
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, false, false);
30+
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings);
3131
$parameters = array('SAMLRequest' => $authnRequest->getRequest());
3232
$authUrl = OneLogin_Saml2_Utils::redirect('http://idp.example.com/SSOService.php', $parameters, true);
3333
$this->assertRegExp('#^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=#', $authUrl);
@@ -39,13 +39,19 @@ public function testCreateDeflatedSAMLRequestURLParameter()
3939
$this->assertRegExp('#^<samlp:AuthnRequest#', $inflated);
4040
}
4141

42+
/**
43+
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
44+
* The creation of a deflated SAML Request with AuthNContext
45+
*
46+
* @covers OneLogin_Saml2_AuthnRequest
47+
*/
4248
public function testAuthNContext()
4349
{
4450
$settingsDir = TEST_ROOT .'/settings/';
4551
include $settingsDir.'settings1.php';
4652

4753
$settings = new OneLogin_Saml2_Settings($settingsInfo);
48-
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings, false, false);
54+
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
4955
$encodedRequest = $authnRequest->getRequest();
5056
$decoded = base64_decode($encodedRequest);
5157
$request = gzinflate($decoded);
@@ -54,7 +60,7 @@ public function testAuthNContext()
5460

5561
$settingsInfo['security']['requestedAuthnContext']= true;
5662
$settings2 = new OneLogin_Saml2_Settings($settingsInfo);
57-
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings2, false, false);
63+
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings2);
5864
$encodedRequest2 = $authnRequest2->getRequest();
5965
$decoded2 = base64_decode($encodedRequest2);
6066
$request2 = gzinflate($decoded2);
@@ -63,7 +69,7 @@ public function testAuthNContext()
6369

6470
$settingsInfo['security']['requestedAuthnContext'] = false;
6571
$settings3 = new OneLogin_Saml2_Settings($settingsInfo);
66-
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings3, false, false);
72+
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings3);
6773
$encodedRequest3 = $authnRequest3->getRequest();
6874
$decoded3 = base64_decode($encodedRequest3);
6975
$request3 = gzinflate($decoded3);
@@ -72,7 +78,7 @@ public function testAuthNContext()
7278

7379
$settingsInfo['security']['requestedAuthnContext']= array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509');
7480
$settings4 = new OneLogin_Saml2_Settings($settingsInfo);
75-
$authnRequest4 = new OneLogin_Saml2_AuthnRequest($settings4, false, false);
81+
$authnRequest4 = new OneLogin_Saml2_AuthnRequest($settings4);
7682
$encodedRequest4 = $authnRequest4->getRequest();
7783
$decoded4 = base64_decode($encodedRequest4);
7884
$request4 = gzinflate($decoded4);
@@ -82,6 +88,68 @@ public function testAuthNContext()
8288
$this->assertContains('<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:X509</saml:AuthnContextClassRef>', $request4);
8389
}
8490

91+
/**
92+
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
93+
* The creation of a deflated SAML Request with ForceAuthn
94+
*
95+
* @covers OneLogin_Saml2_AuthnRequest
96+
*/
97+
public function testForceAuthN()
98+
{
99+
$settingsDir = TEST_ROOT .'/settings/';
100+
include $settingsDir.'settings1.php';
101+
102+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
103+
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
104+
$encodedRequest = $authnRequest->getRequest();
105+
$decoded = base64_decode($encodedRequest);
106+
$request = gzinflate($decoded);
107+
$this->assertNotContains('ForceAuthn="true"', $request);
108+
109+
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings, false, false);
110+
$encodedRequest2 = $authnRequest2->getRequest();
111+
$decoded2 = base64_decode($encodedRequest2);
112+
$request2 = gzinflate($decoded2);
113+
$this->assertNotContains('ForceAuthn="true"', $request2);
114+
115+
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings, true, false);
116+
$encodedRequest3 = $authnRequest3->getRequest();
117+
$decoded3 = base64_decode($encodedRequest3);
118+
$request3 = gzinflate($decoded3);
119+
$this->assertContains('ForceAuthn="true"', $request3);
120+
}
121+
122+
/**
123+
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
124+
* The creation of a deflated SAML Request with isPassive
125+
*
126+
* @covers OneLogin_Saml2_AuthnRequest
127+
*/
128+
public function testIsPassive()
129+
{
130+
$settingsDir = TEST_ROOT .'/settings/';
131+
include $settingsDir.'settings1.php';
132+
133+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
134+
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
135+
$encodedRequest = $authnRequest->getRequest();
136+
$decoded = base64_decode($encodedRequest);
137+
$request = gzinflate($decoded);
138+
$this->assertNotContains('IsPassive="true"', $request);
139+
140+
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings, false, false);
141+
$encodedRequest2 = $authnRequest2->getRequest();
142+
$decoded2 = base64_decode($encodedRequest2);
143+
$request2 = gzinflate($decoded2);
144+
$this->assertNotContains('IsPassive="true"', $request2);
145+
146+
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings, false, true);
147+
$encodedRequest3 = $authnRequest3->getRequest();
148+
$decoded3 = base64_decode($encodedRequest3);
149+
$request3 = gzinflate($decoded3);
150+
$this->assertContains('IsPassive="true"', $request3);
151+
}
152+
85153
/**
86154
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
87155
* The creation of a deflated SAML Request

0 commit comments

Comments
 (0)