Skip to content

Commit 077d5dc

Browse files
committed
Allow specifying a default AuthnContext for the default source
1 parent f07079a commit 077d5dc

4 files changed

Lines changed: 88 additions & 14 deletions

File tree

docs/simplesamlphp-changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Released TBD
1313
* The zone that was selected by the SourceIPSelector is now available in the state.
1414
* The defaultSource for the SourceIPSelector can now be set to `null`. If none of the zones
1515
are matched, a NotFound exception will be thrown.
16+
* It is now possible to set a default AuthnContext in the RequestedAuthnContextSelector.
1617

1718
## Version 2.0.3
1819

modules/core/docs/authsource_selector.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ An example configuration would look like this:
5353
The RequestedAuthnContextSelector is an implementation of the `AbstractSourceSelector` that
5454
uses the RequestedAuthnContext to decide what Authentication Source is called.
5555
It works by defining AuthnContexts with their corresponding Authentication
56-
Sources. The 'default' will be used as a fallback when no RequestedAuthnContext
56+
Sources. The 'default' key will be used as a default when no RequestedAuthnContext
5757
is passed in the request.
5858

5959
An example configuration would look like this:
@@ -63,17 +63,20 @@ An example configuration would look like this:
6363
'core:RequestedAuthnContextSelector',
6464

6565
'contexts' => [
66-
'userpass' => [
66+
10 => [
6767
'identifier' => 'urn:x-simplesamlphp:loa1',
6868
'source' => 'ldap',
6969
],
7070

71-
'mfa' => [
71+
20 => [
7272
'identifier' => 'urn:x-simplesamlphp:loa2',
7373
'source' => 'radius',
7474
],
7575

76-
'default' => 'ldap',
76+
'default' => [
77+
'identifier' => 'urn:x-simplesamlphp:loa0',
78+
'source' => 'sql',
79+
],
7780
],
7881
],
7982
```

modules/core/src/Auth/Source/RequestedAuthnContextSelector.php

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,26 @@ class RequestedAuthnContextSelector extends AbstractSourceSelector
3535
*/
3636
public const SOURCESID = '\SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector.SourceId';
3737

38+
3839
/**
3940
* @var string The default authentication source to use when no RequestedAuthnContext is passed
4041
* @psalm-suppress PropertyNotSetInConstructor
4142
*/
4243
protected string $defaultSource;
4344

4445
/**
45-
* @var array<int, array> An array of AuthnContexts, indexed by its weight (higher = better).
46+
* @var array<int, array> An array of AuthnContexts, indexed by a numeric key.
4647
* Each entry is in the format of:
47-
* `weight` => [`identifier` => 'identifier', `source` => 'source']
48+
* `loa` => [`identifier` => 'identifier', `source` => 'source']
4849
*
4950
* i.e.:
5051
*
51-
* '10' => [
52+
* 10 => [
5253
* 'identifier' => 'urn:x-simplesamlphp:loa1',
5354
* 'source' => 'exampleauth',
5455
* ],
55-
* '20' => [
56+
*
57+
* 20 => [
5658
* 'identifier' => 'urn:x-simplesamlphp:loa2',
5759
* 'source' => 'exampleauth-mfa',
5860
* ]
@@ -73,19 +75,25 @@ public function __construct(array $info, array $config)
7375

7476
Assert::keyExists($config, 'contexts');
7577
Assert::keyExists($config['contexts'], 'default');
76-
Assert::stringNotEmpty($config['contexts']['default']);
77-
$this->defaultSource = $config['contexts']['default'];
78-
unset($config['contexts']['default']);
78+
79+
if (!is_array($config['contexts']['default'])) {
80+
Assert::stringNotEmpty($config['contexts']['default']);
81+
$this->defaultSource = $config['contexts']['default'];
82+
unset($config['contexts']['default']);
83+
}
7984

8085
foreach ($config['contexts'] as $key => $context) {
81-
Assert::natural($key);
86+
($key !== 'default') && Assert::natural($key);
87+
8288
if (!array_key_exists('identifier', $context)) {
8389
throw new Exception(sprintf("Incomplete context '%d' due to missing `identifier` key.", $key));
8490
} elseif (!array_key_exists('source', $context)) {
8591
throw new Exception(sprintf("Incomplete context '%d' due to missing `source` key.", $key));
86-
} else {
87-
$this->contexts[$key] = $context;
8892
}
93+
94+
Assert::stringNotEmpty($context['identifier']);
95+
Assert::stringNotEmpty($context['source']);
96+
$this->contexts[$key] = $context;
8997
}
9098
}
9199

@@ -103,6 +111,12 @@ protected function selectAuthSource(array &$state): string
103111
Logger::info(
104112
"core:RequestedAuthnContextSelector: no RequestedAuthnContext provided; selecting default authsource"
105113
);
114+
115+
if (array_key_exists('default', $this->contexts)) {
116+
$state['saml:AuthnContextClassRef'] = $this->contexts['default']['identifier'];
117+
return $this->contexts['default']['source'];
118+
}
119+
106120
return $this->defaultSource;
107121
}
108122

tests/modules/core/src/Auth/Source/RequestedAuthnContextSelectorTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,62 @@ public static function doAuthentication(Auth\Source $as, array $state): void
162162
}
163163

164164

165+
/**
166+
* Array-syntax
167+
*/
168+
public function testArraySyntaxWorks(): void
169+
{
170+
$sourceConfig = Configuration::loadFromArray([
171+
'selector' => [
172+
'core:RequestedAuthnContextSelector',
173+
174+
'contexts' => [
175+
20 => [
176+
'identifier' => 'urn:x-simplesamlphp:loa2',
177+
'source' => 'loa2',
178+
],
179+
'default' => [
180+
'identifier' => 'urn:x-simplesamlphp:loa1',
181+
'source' => 'loa1',
182+
],
183+
],
184+
],
185+
186+
'loa1' => [
187+
'core:AdminPassword',
188+
],
189+
]);
190+
191+
Configuration::setPreLoadedConfig($sourceConfig, 'authsources.php');
192+
193+
$info = ['AuthId' => 'selector'];
194+
$config = $sourceConfig->getArray('selector');
195+
196+
$selector = new class ($info, $config) extends RequestedAuthnContextSelector {
197+
/**
198+
* @param \SimpleSAML\Auth\Source $as
199+
* @param array $state
200+
* @return void
201+
*/
202+
public static function doAuthentication(Auth\Source $as, array $state): void
203+
{
204+
// Dummy
205+
}
206+
};
207+
208+
$state = [
209+
'saml:RequestedAuthnContext' => [
210+
'AuthnContextClassRef' => ['urn:x-simplesamlphp:loa1'],
211+
'Comparison' => 'exact',
212+
],
213+
];
214+
215+
$selector->authenticate($state);
216+
$this->assertArrayHasKey('saml:AuthnContextClassRef', $state);
217+
$this->assertEquals('urn:x-simplesamlphp:loa1', $state['saml:AuthnContextClassRef']);
218+
}
219+
220+
165221
/**
166222
* Missing source
167223
*/

0 commit comments

Comments
 (0)