20

I'm building an ASP.Net Core 2 PoC for some authentication/authorization discussions/decisions that we need to make.

I'm currently at a point where a user has just defined a new OpenID Provider that this application wants to support.

One way to support this would be to read all of the configured providers during startup and configure them all inside ConfigureServices. But there are tantalising clues that it's also possible to do this without having to kill and restart the app.

IAuthenticationSchemeProvider has an AddScheme method that looks ideal. Now all I need to do is to construct an AuthenticationScheme object and I'm golden. It has a constructor AuthenticationScheme(string name, string displayName, Type handlerType) But I'm not sure how to use the types correctly from Microsoft.AspNetCore.Authentication.OpenIdConnect to correctly construct this object and to allow me to specify the OpenID Connect specific options for this.

I think the type I want to use for the third parameter is OpenIdConnectHandler . But what do I do with my options? (Or in the alternative - how do I get to do the equivalent of being able to supply an Action<OpenIdConnectOptions> delegate)


I found this github issue which is also of interest (no TryAddScheme method, so exceptions are possible, interesting in the abstract if we choose to persue this PoC further) but the small sample doesn't talk about options at all.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • When you register open id connect as usual (via `AddOpenIdConnect()`) - a bunch of stuff is added to DI container. This bunch of stuff, among other important things, includes options (via `IConfigureOptions – Evk Feb 22 '18 at 04:13
  • To clarify above comment a bit: that `handlerType` you pass to authentication scheme constructor will be used to resolve handler from DI container. So that handler itself, and its dependencies (which include options) should also be registered in container. So just adding scheme to `IAuthenticationSchemeProvider ` is not enough. – Evk Feb 22 '18 at 08:40
  • 1
    If you're interested, I developed a package to do that : https://github.com/aguacongas/DymamicAuthProviders – agua from mars Dec 17 '18 at 10:54
  • @aguafrommars Does your package requires to restart the app each time a new configuration is added or modified ? – Dejan Bogatinovski Feb 19 '19 at 09:51
  • 1
    @DejanBogatinovski No, the idea is to not have to restart the app. – agua from mars Feb 19 '19 at 10:04
  • That is exactly what I am looking for - no restarts! I am inspecting your library locally now but I can't find a way to create arbitrary OAuth configuration. It only has 2 buttons for adding either Github or Google. Am I missing something ? – Dejan Bogatinovski Feb 19 '19 at 10:16
  • @DejanBogatinovski Yep, the kind of provider you support (OAuth, JWT...) is define on startup. In the sample, Github is an OAuth provider configured for Github : https://github.com/aguacongas/DymamicAuthProviders/blob/master/sample/Aguacongas.AspNetCore.Authentication.Sample/Startup.cs – agua from mars Feb 19 '19 at 11:10
  • @aguafrommars Okay I see. I need more generic definition of external OAuth providers. I think I will have to modify your library to also get the authorization endpoint, claims etc. from the UI and save it in the database. Then, in the startup class I would only write `dynamicBuilder.AddOAuth("OAuth", "Generic OAuth", options => { });` and `dynamicBuilder.AddOpenIdConnect("OIDC", "Open Id", options => { });` – Dejan Bogatinovski Feb 19 '19 at 11:42
  • @DejanBogatinovski, can you open an issue in the github repo to discuss on this ? – agua from mars Feb 19 '19 at 12:59
  • @Damien_The_Unbeliever, were you able to achieve this functionality ? I am struggling to implement the same – Gopal Zadafiya Jun 26 '19 at 12:55

2 Answers2

17

There's an example of how to do this here - https://github.com/aspnet/AuthSamples/tree/master/samples/DynamicSchemes

Keep in mind that for OAuth schemes, you'll have to do more then just calling schemeProvider.AddScheme and optionsCache.TryAdd - there's also a "postconfigure" step when adding options via the normal method. Here's the class - https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthPostConfigureOptions.cs

So you can register the type OAuthPostConfigureOptions<OAuthOptions, OAuthHandler<OAuthOptions>> into your DI container then grab it via a constructor and call OAuthPostConfigureOptions.PostConfigure on your options before adding the options to the optionsCache.

KevinLamb
  • 634
  • 8
  • 18
Nick
  • 695
  • 8
  • 23
  • 1
    The link provided in this answer is to an old GitHub repository that has been archived. Here is the link to a different repository that has the same example, but I believe is now the source of truth: https://github.com/dotnet/aspnetcore/tree/main/src/Security/samples/DynamicSchemes – Jacob Adams Sep 08 '22 at 19:49
12

To expand upon the above answer (https://stackoverflow.com/a/49825151/2200690), in addition to the schemeProvider.AddScheme and optionsCache.TryAdd in https://github.com/aspnet/AspNetCore/blob/release/2.2/src/Security/samples/DynamicSchemes/Controllers/AuthController.cs, we need to make further changes in Startup.cs and AuthController.cs :

In Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ...
    // register to the DI container so it can be injected in the AuthController constructor.
    services.AddSingleton<OpenIdConnectPostConfigureOptions>();
    ...
}

In AuthController.cs

Inject the OpenIdConnectPostConfigureOptions in the AuthController constructor :

public AuthController(IAuthenticationSchemeProvider schemeProvider, 
    IOptionsMonitorCache<OpenIdConnectOptions> optionsCache,
    OpenIdConnectPostConfigureOptions postConfigureOptions)
{
    _schemeProvider = schemeProvider;
    _optionsCache = optionsCache;
    _postConfigureOptions = postConfigureOptions;
}

in the AddOrUpdate method of AuthController.cs, create an instance of the OpenIdConnectOptions class and then use this in the post configure step :

var options = new OpenIdConnectOptions
{
    Authority = "xxx-endpoint",
    CallbackPath = "/signin-oidc",
    ClientId = "XXX",
    ClientSecret = "XXX",
    ......
};
_postConfigureOptions.PostConfigure("oidc", options);                
_optionsCache.TryAdd("oidc", options);
Suketu Bhuta
  • 1,871
  • 1
  • 18
  • 26