Skip to content

Commit 6de04e0

Browse files
committed
Add support for Subjects on AuthNRequests by the new parameter
1 parent 3f51a32 commit 6de04e0

File tree

5 files changed

+144
-10
lines changed

5 files changed

+144
-10
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,14 @@ $auth = new OneLogin_Saml2_Auth();
647647
$auth->login($newTargetUrl);
648648
```
649649

650-
The login method can receive other five optional parameters:
650+
The login method can receive other six optional parameters:
651651

652652
* `$parameters` - An array of parameters that will be added to the `GET` in the HTTP-Redirect.
653653
* `$forceAuthn` - When true the `AuthNRequest` will set the `ForceAuthn='true'`
654654
* `$isPassive` - When true the `AuthNRequest` will set the `Ispassive='true'`
655655
* `$strict` - True if we want to stay (returns the url string) False to redirect
656656
* `$setNameIdPolicy` - When true the AuthNRequest will set a nameIdPolicy element.
657+
* `$nameIdValueReq` - Indicates to the IdP the subject that should be authenticated.
657658

658659
If a match on the future SAMLResponse ID and the AuthNRequest ID to be sent is required, that AuthNRequest ID must to be extracted and saved.
659660

lib/Saml2/Auth.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,16 +465,17 @@ public function getAttributeWithFriendlyName($friendlyName)
465465
* @param bool $isPassive When true the AuthNRequest will set the Ispassive='true'
466466
* @param bool $stay True if we want to stay (returns the url string) False to redirect
467467
* @param bool $setNameIdPolicy When true the AuthNRueqest will set a nameIdPolicy element
468+
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
468469
*
469470
* @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
470471
*
471472
* @throws OneLogin_Saml2_Error
472473
*/
473-
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true)
474+
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true, $nameIdValueReq = null)
474475
{
475476
assert('is_array($parameters)');
476477

477-
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
478+
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
478479

479480
$this->_lastRequest = $authnRequest->getXML();
480481
$this->_lastRequestID = $authnRequest->getId();

lib/Saml2/AuthnRequest.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ class OneLogin_Saml2_AuthnRequest
2929
* Constructs the AuthnRequest object.
3030
*
3131
* @param OneLogin_Saml2_Settings $settings Settings
32-
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
33-
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
32+
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
33+
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
3434
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
35+
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
3536
*/
36-
public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true)
37+
public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null)
3738
{
3839
$this->_settings = $settings;
3940

@@ -44,6 +45,17 @@ public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = fal
4445
$id = OneLogin_Saml2_Utils::generateUniqueID();
4546
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
4647

48+
$subjectStr = "";
49+
if (isset($nameIdValueReq)) {
50+
$subjectStr = <<<SUBJECT
51+
52+
<saml:Subject>
53+
<saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID>
54+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation>
55+
</saml:Subject>
56+
SUBJECT;
57+
}
58+
4759
$nameIdPolicyStr = '';
4860
if ($setNameIdPolicy) {
4961
$nameIDPolicyFormat = $spData['NameIDFormat'];
@@ -52,6 +64,7 @@ public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = fal
5264
}
5365

5466
$nameIdPolicyStr = <<<NAMEIDPOLICY
67+
5568
<samlp:NameIDPolicy
5669
Format="{$nameIDPolicyFormat}"
5770
AllowCreate="true" />
@@ -93,14 +106,14 @@ public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = fal
93106

94107
$requestedAuthnStr = '';
95108
if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
96-
97109
$authnComparison = 'exact';
98110
if (isset($security['requestedAuthnContextComparison'])) {
99111
$authnComparison = $security['requestedAuthnContextComparison'];
100112
}
101113

102114
if ($security['requestedAuthnContext'] === true) {
103115
$requestedAuthnStr = <<<REQUESTEDAUTHN
116+
104117
<samlp:RequestedAuthnContext Comparison="$authnComparison">
105118
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
106119
</samlp:RequestedAuthnContext>
@@ -127,9 +140,7 @@ public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = fal
127140
Destination="{$idpData['singleSignOnService']['url']}"
128141
ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
129142
AssertionConsumerServiceURL="{$acsUrl}">
130-
<saml:Issuer>{$spEntityId}</saml:Issuer>
131-
{$nameIdPolicyStr}
132-
{$requestedAuthnStr}
143+
<saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr}
133144
</samlp:AuthnRequest>
134145
AUTHNREQUEST;
135146

tests/src/OneLogin/Saml2/AuthTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,90 @@ public function testLoginNameIDPolicy()
11961196
}
11971197
}
11981198

1199+
/**
1200+
* Tests the login method of the OneLogin_Saml2_Auth class
1201+
* Case Login with no parameters. A AuthN Request is built with and without Subject
1202+
*
1203+
* @covers OneLogin_Saml2_Auth::login
1204+
* @runInSeparateProcess
1205+
*/
1206+
public function testLoginSubject()
1207+
{
1208+
$settingsDir = TEST_ROOT .'/settings/';
1209+
include $settingsDir.'settings1.php';
1210+
1211+
$auth = new OneLogin_Saml2_Auth($settingsInfo);
1212+
1213+
try {
1214+
// The Header of the redirect produces an Exception
1215+
$returnTo = 'http://example.com/returnto';
1216+
$auth->login($returnTo);
1217+
// Do not ever get here
1218+
$this->assertFalse(true);
1219+
} catch (Exception $e) {
1220+
$this->assertContains('Cannot modify header information', $e->getMessage());
1221+
$trace = $e->getTrace();
1222+
$targetUrl = getUrlFromRedirect($trace);
1223+
$parsedQuery = getParamsFromUrl($targetUrl);
1224+
1225+
$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
1226+
$this->assertContains($ssoUrl, $targetUrl);
1227+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
1228+
$encodedRequest = $parsedQuery['SAMLRequest'];
1229+
$decoded = base64_decode($encodedRequest);
1230+
$request = gzinflate($decoded);
1231+
$this->assertNotContains('<saml:Subject', $request);
1232+
}
1233+
1234+
try {
1235+
// The Header of the redirect produces an Exception
1236+
$returnTo = 'http://example.com/returnto';
1237+
$auth->login($returnTo, array(), false, false, false, true, "[email protected]");
1238+
// Do not ever get here
1239+
$this->assertFalse(true);
1240+
} catch (Exception $e) {
1241+
$this->assertContains('Cannot modify header information', $e->getMessage());
1242+
$trace2 = $e->getTrace();
1243+
$targetUrl2 = getUrlFromRedirect($trace2);
1244+
$parsedQuery2 = getParamsFromUrl($targetUrl2);
1245+
1246+
$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
1247+
$this->assertContains($ssoUrl2, $targetUrl2);
1248+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
1249+
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
1250+
$decoded2 = base64_decode($encodedRequest2);
1251+
$request2 = gzinflate($decoded2);
1252+
$this->assertContains('<saml:Subject', $request2);
1253+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">[email protected]</saml:NameID>', $request2);
1254+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request2);
1255+
}
1256+
1257+
try {
1258+
// The Header of the redirect produces an Exception
1259+
$returnTo = 'http://example.com/returnto';
1260+
$settingsInfo['sp']['NameIDFormat'] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
1261+
$auth2 = new OneLogin_Saml2_Auth($settingsInfo);
1262+
$auth2->login($returnTo);
1263+
// Do not ever get here
1264+
$this->assertFalse(true);
1265+
} catch (Exception $e) {
1266+
$this->assertContains('Cannot modify header information', $e->getMessage());
1267+
$trace3 = $e->getTrace();
1268+
$targetUrl3 = getUrlFromRedirect($trace3);
1269+
$parsedQuery3 = getParamsFromUrl($targetUrl3);
1270+
1271+
$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
1272+
$this->assertContains($ssoUrl3, $targetUrl3);
1273+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
1274+
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
1275+
$decoded3 = base64_decode($encodedRequest3);
1276+
$request3 = gzinflate($decoded3);
1277+
$this->assertContains('<saml:Subject', $request3);
1278+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml:NameID>', $request3);
1279+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request3);
1280+
}
1281+
}
1282+
11991283
/**
12001284
* Tests the logout method of the OneLogin_Saml2_Auth class
12011285
* Case Logout with no parameters. A logout Request is built and redirect executed

tests/src/OneLogin/Saml2/AuthnRequestTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,43 @@ public function testNameIDPolicy()
189189
$this->assertContains('<samlp:NameIDPolicy', $request3);
190190
}
191191

192+
/**
193+
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
194+
* The creation of a deflated SAML Request with and without Subject
195+
*
196+
* @covers OneLogin_Saml2_AuthnRequest
197+
*/
198+
public function testSubject()
199+
{
200+
$settingsDir = TEST_ROOT .'/settings/';
201+
include $settingsDir.'settings1.php';
202+
203+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
204+
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
205+
$encodedRequest = $authnRequest->getRequest();
206+
$decoded = base64_decode($encodedRequest);
207+
$request = gzinflate($decoded);
208+
$this->assertNotContains('<saml:Subject', $request);
209+
210+
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings, false, false, true, "[email protected]");
211+
$encodedRequest2 = $authnRequest2->getRequest();
212+
$decoded2 = base64_decode($encodedRequest2);
213+
$request2 = gzinflate($decoded2);
214+
$this->assertContains('<saml:Subject', $request2);
215+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">[email protected]</saml:NameID>', $request2);
216+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request2);
217+
218+
$settingsInfo['sp']['NameIDFormat'] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
219+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
220+
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings, false, false, true, "[email protected]");
221+
$encodedRequest3 = $authnRequest3->getRequest();
222+
$decoded3 = base64_decode($encodedRequest3);
223+
$request3 = gzinflate($decoded3);
224+
$this->assertContains('<saml:Subject', $request3);
225+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml:NameID>', $request3);
226+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request3);
227+
}
228+
192229
/**
193230
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
194231
* The creation of a deflated SAML Request

0 commit comments

Comments
 (0)