如何在 AddOpenIdConnect 事件 (OnTokenValidated) 中注入服务?

How to inject services in an AddOpenIdConnect event (OnTokenValidated)?

我需要编写需要 ConfigureServices 才能完全执行的业务逻辑。 OnTokenValidatedConfigureServices 本身中的位置很糟糕。

What is the alternative of AddOpenIdConnect middleware so that I can Success call with full flexibility to use injected services?

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "cookie";
            //...
        })
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = "https://localhost:5001";
            /....
            options.Scope.Add("offline_access");

            options.Events.OnTokenValidated = async n =>
            {
                //Need other services which will only 
                //get injected once ConfigureServices method has fully executed.
            };
        }

参考:为什么 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#ASP0000

您可以使用 IAuthenticationSchemeProviderIOptionsMonitorCache 在运行时添加身份验证方案。

public class AuthInitializer
{
    private IAuthenticationSchemeProvider _provider;
    private IOptionsMonitorCache<OpenIdConnectOptions> _optionsCache;
    // other services you need

    public AuthInitializer(IAuthenticationSchemeProvider provider, IOptionsMonitorCache<OpenIdConnectOptions> options)
    {
        _provider = provider;
        _optionsCache = options;
    }

    public Task InitializeAsync(CancellationToken cancellationToken = default)
    {
        // execute some business logic
        // ...

        var schemeName = "OidcCustom1"; // must be unique for different schemes
        var schemeOptions = new OpenIdConnectOptions()
        {
            Authority = "https://demo.identityserver.io/",
            ClientId = "m2m", // fetch credentials from another service or database
            ClientSecret = "secret",
            Events = {
                OnTokenValidated = async context => {
                    var someService = context.HttpContext.RequestServices.GetRequiredService<ISomeService>();
                    var result = await someService.DoSomethingAsync();
                    // handle the event
                }
            }
        };
    
        var scheme = new AuthenticationScheme(schemeName, displayName:null, typeof(OpenIdConnectHandler));
        _provider.TryAddScheme(scheme);
        _optionsCache.TryAdd(
            schemeName,
            schemeOptions
        );
        return Task.CompletedTask;
    }
}

在 DI 中注册这个 class:

services.AddTransient<AuthInitializer>();

然后在构建主机后执行它:

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
        using var scope = host.Services.CreateScope();
        var authInitializer = scope.ServiceProvider.GetRequiredService<AuthInitializer>();
        await authInitializer.InitializeAsync();
        await host.RunAsync();
    }
}

那你可以照常参考这个auth scheme:

services.AddAuthentication(
    options =>
    {
        options.DefaultScheme = "OidcCustom";
    });

[Authorize(AuthenticationSchemes = "OidcCustom")]
[HttpGet]
public async Task<IActionResult> Index(CancellationToken cancellationToken) { ... }

要在 OnTokenValidated 事件处理程序中解析服务,您可以使用 TokenValidatedContext.HttpContext.RequestServices:

Events =
{
    OnTokenValidated = async context =>
    {
        var someService = context.HttpContext.RequestServices.GetRequiredService<ISomeService>();
        var result = await someService.DoSomethingAsync();
        // a custom callback
    }
}