Skip to content

Commit 9922fee

Browse files
committed
Add support for Subjects on AuthNRequests by the new parameter nameIdValueReq
1 parent c9026b2 commit 9922fee

5 files changed

Lines changed: 146 additions & 12 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,13 +602,14 @@ $auth = new OneLogin\Saml2\Auth();
602602
$auth->login($newTargetUrl);
603603
```
604604

605-
The login method can receive other five optional parameters:
605+
The login method can receive other six optional parameters:
606606

607607
* `$parameters` - An array of parameters that will be added to the `GET` in the HTTP-Redirect.
608608
* `$forceAuthn` - When true the `AuthNRequest` will set the `ForceAuthn='true'`
609609
* `$isPassive` - When true the `AuthNRequest` will set the `Ispassive='true'`
610610
* `$strict` - True if we want to stay (returns the url string) False to redirect
611611
* `$setNameIdPolicy` - When true the AuthNRequest will set a nameIdPolicy element.
612+
* `$nameIdValueReq` - Indicates to the IdP the subject that should be authenticated.
612613

613614
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.
614615

src/Saml2/Auth.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,14 +520,15 @@ public function getAttributeWithFriendlyName($friendlyName)
520520
* @param bool $isPassive When true the AuthNRequest will set the Ispassive='true'
521521
* @param bool $stay True if we want to stay (returns the url string) False to redirect
522522
* @param bool $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element
523+
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
523524
*
524525
* @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
525-
*
526+
*
526527
* @throws Error
527528
*/
528-
public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true)
529+
public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true, $nameIdValueReq = null)
529530
{
530-
$authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
531+
$authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
531532

532533
$this->_lastRequest = $authnRequest->getXML();
533534
$this->_lastRequestID = $authnRequest->getId();

src/Saml2/AuthnRequest.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ class AuthnRequest
4444
/**
4545
* Constructs the AuthnRequest object.
4646
*
47-
* @param Settings $settings SAML Toolkit Settings
48-
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
49-
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
50-
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
47+
* @param Settings $settings SAML Toolkit Settings
48+
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
49+
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
50+
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
51+
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
5152
*/
52-
public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true)
53+
public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null)
5354
{
5455
$this->_settings = $settings;
5556

@@ -60,6 +61,17 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = fa
6061
$id = Utils::generateUniqueID();
6162
$issueInstant = Utils::parseTime2SAML(time());
6263

64+
$subjectStr = "";
65+
if (isset($nameIdValueReq)) {
66+
$subjectStr = <<<SUBJECT
67+
68+
<saml:Subject>
69+
<saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID>
70+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation>
71+
</saml:Subject>
72+
SUBJECT;
73+
}
74+
6375
$nameIdPolicyStr = '';
6476
if ($setNameIdPolicy) {
6577
$nameIDPolicyFormat = $spData['NameIDFormat'];
@@ -68,6 +80,7 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = fa
6880
}
6981

7082
$nameIdPolicyStr = <<<NAMEIDPOLICY
83+
7184
<samlp:NameIDPolicy
7285
Format="{$nameIDPolicyFormat}"
7386
AllowCreate="true" />
@@ -116,6 +129,7 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = fa
116129

117130
if ($security['requestedAuthnContext'] === true) {
118131
$requestedAuthnStr = <<<REQUESTEDAUTHN
132+
119133
<samlp:RequestedAuthnContext Comparison="$authnComparison">
120134
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
121135
</samlp:RequestedAuthnContext>
@@ -142,9 +156,7 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = fa
142156
Destination="{$idpData['singleSignOnService']['url']}"
143157
ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
144158
AssertionConsumerServiceURL="{$acsUrl}">
145-
<saml:Issuer>{$spEntityId}</saml:Issuer>
146-
{$nameIdPolicyStr}
147-
{$requestedAuthnStr}
159+
<saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr}
148160
</samlp:AuthnRequest>
149161
AUTHNREQUEST;
150162

tests/src/OneLogin/Saml2/AuthTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,89 @@ public function testLoginNameIDPolicy()
12741274
}
12751275
}
12761276

1277+
/**
1278+
* Tests the login method of the Auth class
1279+
* Case Login with no parameters. A AuthN Request is built with and without Subject
1280+
*
1281+
* @covers OneLogin\Saml2\Auth::login
1282+
* @runInSeparateProcess
1283+
*/
1284+
public function testLoginSubject()
1285+
{
1286+
$settingsDir = TEST_ROOT .'/settings/';
1287+
include $settingsDir.'settings1.php';
1288+
$auth = new Auth($settingsInfo);
1289+
1290+
try {
1291+
// The Header of the redirect produces an Exception
1292+
$returnTo = 'http://example.com/returnto';
1293+
$auth->login($returnTo);
1294+
// Do not ever get here
1295+
$this->assertFalse(true);
1296+
} catch (Exception $e) {
1297+
$this->assertContains('Cannot modify header information', $e->getMessage());
1298+
$trace = $e->getTrace();
1299+
$targetUrl = getUrlFromRedirect($trace);
1300+
$parsedQuery = getParamsFromUrl($targetUrl);
1301+
1302+
$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
1303+
$this->assertContains($ssoUrl, $targetUrl);
1304+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
1305+
$encodedRequest = $parsedQuery['SAMLRequest'];
1306+
$decoded = base64_decode($encodedRequest);
1307+
$request = gzinflate($decoded);
1308+
$this->assertNotContains('<saml:Subject', $request);
1309+
}
1310+
1311+
try {
1312+
// The Header of the redirect produces an Exception
1313+
$returnTo = 'http://example.com/returnto';
1314+
$auth->login($returnTo, array(), false, false, false, true, "testuser@example.com");
1315+
// Do not ever get here
1316+
$this->assertFalse(true);
1317+
} catch (Exception $e) {
1318+
$this->assertContains('Cannot modify header information', $e->getMessage());
1319+
$trace2 = $e->getTrace();
1320+
$targetUrl2 = getUrlFromRedirect($trace2);
1321+
$parsedQuery2 = getParamsFromUrl($targetUrl2);
1322+
1323+
$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
1324+
$this->assertContains($ssoUrl2, $targetUrl2);
1325+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
1326+
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
1327+
$decoded2 = base64_decode($encodedRequest2);
1328+
$request2 = gzinflate($decoded2);
1329+
$this->assertContains('<saml:Subject', $request2);
1330+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">testuser@example.com</saml:NameID>', $request2);
1331+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request2);
1332+
}
1333+
1334+
try {
1335+
// The Header of the redirect produces an Exception
1336+
$returnTo = 'http://example.com/returnto';
1337+
$settingsInfo['sp']['NameIDFormat'] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
1338+
$auth2 = new Auth($settingsInfo);
1339+
$auth2->login($returnTo);
1340+
// Do not ever get here
1341+
$this->assertFalse(true);
1342+
} catch (Exception $e) {
1343+
$this->assertContains('Cannot modify header information', $e->getMessage());
1344+
$trace3 = $e->getTrace();
1345+
$targetUrl3 = getUrlFromRedirect($trace3);
1346+
$parsedQuery3 = getParamsFromUrl($targetUrl3);
1347+
1348+
$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
1349+
$this->assertContains($ssoUrl3, $targetUrl3);
1350+
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
1351+
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
1352+
$decoded3 = base64_decode($encodedRequest3);
1353+
$request3 = gzinflate($decoded3);
1354+
$this->assertContains('<saml:Subject', $request3);
1355+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testuser@example.com</saml:NameID>', $request3);
1356+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request3);
1357+
}
1358+
}
1359+
12771360
/**
12781361
* Tests the logout method of the Auth class
12791362
* 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
@@ -195,6 +195,43 @@ public function testNameIDPolicy()
195195
$this->assertContains('<samlp:NameIDPolicy', $request3);
196196
}
197197

198+
/**
199+
* Tests the AuthnRequest Constructor.
200+
* The creation of a deflated SAML Request with and without Subject
201+
*
202+
* @covers OneLogin\Saml2\AuthnRequest
203+
*/
204+
public function testSubject()
205+
{
206+
$settingsDir = TEST_ROOT .'/settings/';
207+
include $settingsDir.'settings1.php';
208+
209+
$settings = new Settings($settingsInfo);
210+
$authnRequest = new AuthnRequest($settings);
211+
$encodedRequest = $authnRequest->getRequest();
212+
$decoded = base64_decode($encodedRequest);
213+
$request = gzinflate($decoded);
214+
$this->assertNotContains('<saml:Subject', $request);
215+
216+
$authnRequest2 = new AuthnRequest($settings, false, false, true, "testuser@example.com");
217+
$encodedRequest2 = $authnRequest2->getRequest();
218+
$decoded2 = base64_decode($encodedRequest2);
219+
$request2 = gzinflate($decoded2);
220+
$this->assertContains('<saml:Subject', $request2);
221+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">testuser@example.com</saml:NameID>', $request2);
222+
223+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request2);
224+
$settingsInfo['sp']['NameIDFormat'] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
225+
$settings = new Settings($settingsInfo);
226+
$authnRequest3 = new AuthnRequest($settings, false, false, true, "testuser@example.com");
227+
$encodedRequest3 = $authnRequest3->getRequest();
228+
$decoded3 = base64_decode($encodedRequest3);
229+
$request3 = gzinflate($decoded3);
230+
$this->assertContains('<saml:Subject', $request3);
231+
$this->assertContains('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testuser@example.com</saml:NameID>', $request3);
232+
$this->assertContains('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', $request3);
233+
}
234+
198235
/**
199236
* Tests the AuthnRequest Constructor.
200237
* The creation of a deflated SAML Request

0 commit comments

Comments
 (0)