Skip to content

Commit 6cca5e5

Browse files
authored
Add configurable default RequestedAuthnContext
Add a configurable default RequestedAuthnContext so that it is sent to the IdP. But only if the SP does not already request it, or if an Mfa rule is configured for the SP.
1 parent 511261e commit 6cca5e5

File tree

9 files changed

+71
-3
lines changed

9 files changed

+71
-3
lines changed

library/EngineBlock/Corto/ProxyServer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ public function sendAuthenticationRequest(
426426
// entity which does not reside in the database.
427427
$originalSpEnitytId = $spEntityId;
428428
}
429+
429430
// Add authncontextclassref if configured
430431
$service = $identityProvider->getCoins()->mfaEntities()->findByEntityId($originalSpEnitytId);
431432
if ($service instanceof MfaEntity) {
@@ -438,6 +439,16 @@ public function sendAuthenticationRequest(
438439
// When transparent_authn_context is configured, forward the SP AuthnClassRefContext to the IdP
439440
// See: https://www.pivotaltracker.com/story/show/173305494
440441
$sspMessage->setRequestedAuthnContext($spRequest->getRequestedAuthnContext());
442+
} elseif ($spRequest->getRequestedAuthnContext() === null) {
443+
// The request didn't have a RequestedAuthnContext set, but we do have a default to fall back to
444+
$defaultRAC = $identityProvider->getCoins()->defaultRAC();
445+
if ($defaultRAC !== null) {
446+
$sspMessage->setRequestedAuthnContext([
447+
'AuthnContextClassRef' => [
448+
$defaultRAC,
449+
],
450+
]);
451+
}
441452
}
442453

443454
$authenticationState = EngineBlock_ApplicationSingleton::getInstance()->getDiContainer()

src/OpenConext/EngineBlock/Metadata/Coins.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ public static function createForIdentityProvider(
7575
$disableScoping,
7676
$additionalLogging,
7777
$signatureMethod,
78-
$mfaEntities
78+
$mfaEntities,
79+
$defaultRAC
7980
) {
8081
return new self([
8182
'guestQualifier' => $guestQualifier,
@@ -86,6 +87,7 @@ public static function createForIdentityProvider(
8687
'signatureMethod' => $signatureMethod,
8788
'stepupConnections' => $stepupConnections,
8889
'mfaEntities' => $mfaEntities,
90+
'defaultRAC' => $defaultRAC,
8991
]);
9092
}
9193

@@ -192,6 +194,11 @@ public function isStepupForceAuthn()
192194
}
193195

194196
// IDP
197+
public function defaultRAC()
198+
{
199+
return $this->getValue('defaultRAC');
200+
}
201+
195202
public function guestQualifier()
196203
{
197204
return $this->getValue('guestQualifier', IdentityProvider::GUEST_QUALIFIER_ALL);

src/OpenConext/EngineBlock/Metadata/Entity/Assembler/PushMetadataAssembler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ private function assembleIdp(stdClass $connection)
267267
$properties += $this->assembleMfaEntities($connection);
268268

269269
$properties += $this->discoveryAssembler->assembleDiscoveries($connection);
270+
$properties += $this->setPathFromObjectString(array($connection, 'metadata:coin:defaultRAC'), 'defaultRAC');
270271

271272
return Utils::instantiate(
272273
IdentityProvider::class,

src/OpenConext/EngineBlock/Metadata/Entity/Disassembler/CortoDisassembler.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ public function translateIdentityProvider(IdentityProvider $entity)
122122
);
123123
}
124124

125+
if ($entity->getCoins()->defaultRAC()) {
126+
$cortoEntity['DefaultRAC'] = $entity->getCoins()->defaultRAC();
127+
}
128+
125129
return $cortoEntity;
126130
}
127131

src/OpenConext/EngineBlock/Metadata/Entity/IdentityProvider.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ public function __construct(
143143
ConsentSettings $consentSettings = null,
144144
StepupConnections $stepupConnections = null,
145145
MfaEntityCollection $mfaEntities = null,
146-
array $discoveries = []
146+
array $discoveries = [],
147+
?string $defaultRAC = null
147148
) {
148149
if (is_null($mdui)) {
149150
$mdui = Mdui::emptyMdui();
@@ -190,7 +191,8 @@ public function __construct(
190191
$disableScoping,
191192
$additionalLogging,
192193
$signatureMethod,
193-
$mfaEntities
194+
$mfaEntities,
195+
$defaultRAC
194196
);
195197

196198
$this->assertAllDiscoveries($discoveries);

src/OpenConext/EngineBlockFunctionalTestingBundle/Features/Context/MfaEntitiesContext.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,17 @@ public function setIdpConfiguredMfaAuthnMethodFor($idpName, $authncontextclassre
7171
]])
7272
->save();
7373
}
74+
75+
/**
76+
* @Given /^the IdP "([^"]*)" has a default RequestedAuthnContext configured as "([^"]*)"$/
77+
*/
78+
public function setIdpConfiguredAuthnContext(string $idpName, string $authnContextClassRef): void
79+
{
80+
/** @var MockIdentityProvider $mockIdp */
81+
$mockIdp = $this->mockIdpRegistry->get($idpName);
82+
83+
$this->serviceRegistryFixture
84+
->setDefaultRequestedAuthContext($mockIdp->entityId(), $authnContextClassRef)
85+
->save();
86+
}
7487
}

src/OpenConext/EngineBlockFunctionalTestingBundle/Features/Context/MockSpContext.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,4 +688,15 @@ public function theSPSendsAuthnContextClassRefWithValue($spName, $authnContextCl
688688
$request->setRequestedAuthnContext(['AuthnContextClassRef' => [$authnContextClassRefValue]]);
689689
$this->mockSpRegistry->save();
690690
}
691+
692+
/**
693+
* @Given /^the SP "([^"]*)" sends no AuthnContextClassRef$/
694+
*/
695+
public function theSPSendsNoAuthnContextClassRef(string $spName): void
696+
{
697+
$mockSp = $this->mockSpRegistry->get($spName);
698+
$request = $mockSp->getAuthnRequest();
699+
$request->setRequestedAuthnContext();
700+
$this->mockSpRegistry->save();
701+
}
691702
}

src/OpenConext/EngineBlockFunctionalTestingBundle/Features/MfaAuthnContextClassRef.feature

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,15 @@ Feature:
103103
Then the url should match "/functional-testing/SSO-SP/acs"
104104
And the response should contain "urn:oasis:names:tc:SAML:2.0:status:Responder"
105105
And the response should contain "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"
106+
107+
Scenario: A login should succeed if the we don't have a RequestedAuthnContext but a default authn class ref is configured
108+
Given the IdP "SSO-IdP" has a default RequestedAuthnContext configured as "http://default-context.example.com/authn"
109+
And the SP "SSO-SP" sends no AuthnContextClassRef
110+
When I log in at "SSO-SP"
111+
And I pass through EngineBlock
112+
Then the url should match "functional-testing/SSO-IdP/sso"
113+
And the response should contain "http://default-context.example.com/authn"
114+
And I pass through the IdP
115+
And I give my consent
116+
And I pass through EngineBlock
117+
Then the url should match "/functional-testing/SSO-SP/acs"

src/OpenConext/EngineBlockFunctionalTestingBundle/Fixtures/ServiceRegistryFixture.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,13 @@ public function setMfaEntities($entityId, array $mfaEntities)
492492
return $this;
493493
}
494494

495+
public function setDefaultRequestedAuthContext(string $entityId, string $defaultRAC): self
496+
{
497+
$this->setCoin($this->getIdentityProvider($entityId), 'defaultRAC', $defaultRAC);
498+
499+
return $this;
500+
}
501+
495502
public function setIdpLogo($entityId, $url)
496503
{
497504
$logo = new Logo($url);

0 commit comments

Comments
 (0)