Как внедрить сервисы в событие AddOpenIdConnect (OnTokenValidated)?

Мне нужно написать и бизнес-логику, которая требует полного выполнения. OnTokenValidated плохо расположен внутри ConfigureServices сам.

Какая альтернатива AddOpenIdConnect промежуточное ПО, чтобы я мог Success звонить с полной гибкостью, чтобы пользоваться инъекционными услугами?

              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

2 ответа

Решение

Чтобы разрешить услуги внутри OnTokenValidated обработчик событий, вы можете использовать TokenValidatedContext.HttpContext.RequestServices:

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

Вы можете добавить схему аутентификации во время выполнения, используя IAuthenticationSchemeProvider и IOptionsMonitorCache.

      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:

      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();
    }
}

Затем вы можете обратиться к этой схеме авторизации как обычно:

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

или

      [Authorize(AuthenticationSchemes = "OidcCustom")]
[HttpGet]
public async Task<IActionResult> Index(CancellationToken cancellationToken) { ... }
Другие вопросы по тегам