The Scheme metadata in the log event will indicate which scheme the authentication handler is using. This will help users observe that hot reload has succeeded.
How was this tested?
[x] Manual tests with real identity provider that we don't have in our pipelines. This is a development mode only feature.
Scenario 1 - Swap JWT Provider Details
Open the dab-config.json file used by DAB.
Ensure the config sets Mode: Development
Modify the Authentication section to include JWT authN with your AzureAD provider (issuer/audience).
Send a request which requires validation: see HTTP 200
Modify the Authentication section with an "invalid" audience
Select "SAVE" -> hot reload will occur
Send a new request using the previously used token -> HTTP 401 invalid issuer.
Scenario 2 - Swap from JWT to Simulator
Open the dab-config.json file used by DAB.
Ensure the config sets Mode: Development
Modify the Authentication section to include JWT authN with your AzureAD provider (issuer/audience).
Send a request which requires validation: see HTTP 200
Modify the Authentication section by removing the jwt section with audience/issuer and leave only the "Provider" property
Modify the authentication provider property to be Simulator
Select "SAVE" -> hot reload will occur
Send a new request using the previously used token -> authenticated
Usage of ChangeTokens versus EventHandler in this specific workstream:
The objective was to trigger the IOptionsMonitor within the JwtBearerHandler to detect changes to authentication programmatically (as opposed to the out-of-box change detection for a bound configuration file such as appsettings.json, which we don't use). In order to alert the IOptionsMonitor that a change has occurred, I needed to register:
public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
{
_factory = factory;
_cache = cache;
void RegisterSource(IOptionsChangeTokenSource<TOptions> source)
{
IDisposable registration = ChangeToken.OnChange(
source.GetChangeToken,
InvokeChanged,
source.Name);
_registrations.Add(registration);
}
// The default DI container uses arrays under the covers. Take advantage of this knowledge
// by checking for an array and enumerate over that, so we don't need to allocate an enumerator.
if (sources is IOptionsChangeTokenSource<TOptions>[] sourcesArray)
{
foreach (IOptionsChangeTokenSource<TOptions> source in sourcesArray)
{
RegisterSource(source);
}
once our runtimeconfigloader detects a file change, it signals the provider that new runtimeconfig is available. Then the RuntimeConfigProvider (which is the service resolved via DI in most of our classes already) can signal the change token whose listener is the OptionsMonitor via registering the IOptionsChangeTokenSource.
This mechanism was not as intrusive to our services' constructors:
No need to resolve IOptionsMonitorCache<jwtBearerOptions> in our RuntimeConfigProvider or RuntimeConfigLoader class constructors. Updating the optionsmonitorcache would be( i tested this) the manual way of changing IOptionsMonitor<JwtBearerOptions> used by the JwtBearerHandler, but this didn't work because of the next bullet point:
I made a judgement call that injecting IoptionsmonitorCache<jwtBearerOptions> into either of the two referenced classes seemed to leak implementation details of the provider/loader and wouldn't be straightforward without more time consuming and costly refactors:
Because both the provider/loader are manually instantiated in Startup::ConfigureServices and don't have the opportunity to resolve services via DI.
Because we'd then need to make even more cascading changes in the test projects to accommodate setting up the ioptionsmonitorcache object to then be added to our mock runtimeconfigprovider/loader objects.
The event-handlers we have recently added don't fit this specific use case of updating the underlying authentication configuration. In addition to the above design decisions, I didn't identify a solution where we could subscribe the JwtBearerHandler's OptionsMonitor to an eventhandler signal to notify that a change occurred. I pursued using the built-in mechanism of change detection for optionsmonitor provided to us via IOptionsChangeTokenSource.
Why make this change?
What is this change?
The following changes are only honored in development mode when testing locally using "Hot Reload"
ConfigureAuthenticationV2()
in startup.cs which wires up all DAB supported Authentication providers:Update ClientRoleHeaderAuthenticationMiddleware to emit condensed and more helpful log event for each request:
How was this tested?
Scenario 1 - Swap JWT Provider Details
Mode: Development
Scenario 2 - Swap from JWT to Simulator
Mode: Development
Usage of ChangeTokens versus EventHandler in this specific workstream:
The objective was to trigger the
IOptionsMonitor
within theJwtBearerHandler
to detect changes to authentication programmatically (as opposed to the out-of-box change detection for a bound configuration file such asappsettings.json
, which we don't use). In order to alert theIOptionsMonitor
that a change has occurred, I needed to register:which is this resolved via DI in the
OptionsMonitor
class: https://github.com/dotnet/runtime/blob/88397049d6aa2c8505bd11309ddd314169e3f73f/src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs#L31-L53once our
runtimeconfigloader
detects a file change, it signals the provider that newruntimeconfig
is available. Then theRuntimeConfigProvider
(which is the service resolved via DI in most of our classes already) can signal the change token whose listener is theOptionsMonitor
via registering theIOptionsChangeTokenSource
.This mechanism was not as intrusive to our services' constructors:
IOptionsMonitorCache<jwtBearerOptions>
in ourRuntimeConfigProvider
orRuntimeConfigLoader
class constructors. Updating theoptionsmonitorcache
would be( i tested this) the manual way of changingIOptionsMonitor<JwtBearerOptions>
used by theJwtBearerHandler
, but this didn't work because of the next bullet point:IoptionsmonitorCache<jwtBearerOptions>
into either of the two referenced classes seemed to leak implementation details of the provider/loader and wouldn't be straightforward without more time consuming and costly refactors:Startup::ConfigureServices
and don't have the opportunity to resolve services via DI.ioptionsmonitorcache
object to then be added to our mock runtimeconfigprovider/loader objects.The event-handlers we have recently added don't fit this specific use case of updating the underlying authentication configuration. In addition to the above design decisions, I didn't identify a solution where we could subscribe the
JwtBearerHandler
'sOptionsMonitor
to an eventhandler signal to notify that a change occurred. I pursued using the built-in mechanism of change detection for optionsmonitor provided to us viaIOptionsChangeTokenSource
.