******************************************************************************
.UseAuthenticationʱע֤м

public class AuthenticationMiddleware
{
    private readonly RequestDelegate _next;

    public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
    {
        if (next == null)
        {
            throw new ArgumentNullException(nameof(next));
        }
        if (schemes == null)
        {
            throw new ArgumentNullException(nameof(schemes));
        }

        _next = next;
        Schemes = schemes;
    }

	// ΪAuthenticationSchemeProviderAddAuthenticationCoreʱע
    public IAuthenticationSchemeProvider Schemes { get; set; }

    public async Task Invoke(HttpContext context)
    {
		// ضʱ
        context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
        {
            OriginalPath = context.Request.Path,
            OriginalPathBase = context.Request.PathBase
        });

		// Զ֤磺OAuth, OIDC
        var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
        foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
        {
			// AuthenticationHandlerProviderڲѯSchemesHandler
            var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
            if (handler != null && await handler.HandleRequestAsync())
            {
                return;
            }
        }

		// ֤
        var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
            if (result?.Principal != null)
            {
                context.User = result.Principal;
            }
        }

        await _next(context);
    }
}

public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
{
    private readonly AuthenticationOptions _options;
    private readonly object _lock = new object();

    private readonly IDictionary<string, AuthenticationScheme> _schemes;
    private readonly List<AuthenticationScheme> _requestHandlers;
    // Used as a safe return value for enumeration apis
    private IEnumerable<AuthenticationScheme> _schemesCopy = Array.Empty<AuthenticationScheme>();
    private IEnumerable<AuthenticationScheme> _requestHandlersCopy = Array.Empty<AuthenticationScheme>();

	public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options)
            : this(options, new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal))
    {
    }

    protected AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes)
    {
        _options = options.Value;

        _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
        _requestHandlers = new List<AuthenticationScheme>();

        foreach (var builder in _options.Schemes)
        {
            var scheme = builder.Build();
            AddScheme(scheme);
        }
    }

	private Task<AuthenticationScheme> GetDefaultSchemeAsync()
		=> _options.DefaultScheme != null
		? GetSchemeAsync(_options.DefaultScheme)
		: Task.FromResult<AuthenticationScheme>(null);

	public virtual Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
		=> _options.DefaultAuthenticateScheme != null
		? GetSchemeAsync(_options.DefaultAuthenticateScheme)
		: GetDefaultSchemeAsync();

	public virtual Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync()
        => Task.FromResult(_requestHandlersCopy);

	public virtual void AddScheme(AuthenticationScheme scheme)
    {
        if (_schemes.ContainsKey(scheme.Name))
        {
            throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
        }
        lock (_lock)
        {
            if (_schemes.ContainsKey(scheme.Name))
            {
                throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
            }
            if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType))
            {
                _requestHandlers.Add(scheme);
                _requestHandlersCopy = _requestHandlers.ToArray();
            }
            _schemes[scheme.Name] = scheme;
            _schemesCopy = _schemes.Values.ToArray();
        }
    }
}


******************************************************************************
.AddAuthenticationע֤
public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    services.AddAuthenticationCore();
    services.AddDataProtection();
    services.AddWebEncoders();
    services.TryAddSingleton<ISystemClock, SystemClock>();
    return new AuthenticationBuilder(services);
}

public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, string defaultScheme)
    => services.AddAuthentication(o => o.DefaultScheme = defaultScheme);

public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    if (configureOptions == null)
    {
        throw new ArgumentNullException(nameof(configureOptions));
    }

    var builder = services.AddAuthentication();
    services.Configure(configureOptions);
    return builder;
}


public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
    services.TryAddScoped<IAuthenticationService, AuthenticationService>();
    services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
    services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
    services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
    return services;
}


******************************************************************************
AuthenticationBuilderչAddCookieAddOpenIdConnectAddJwtBearerʱڲʵһAuthenticationScheme
磺AddJwtBearer->AddScheme<JwtBearerOptions, JwtBearerHandler>
private AuthenticationBuilder AddSchemeHelper<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
    where TOptions : class, new()
    where THandler : class, IAuthenticationHandler
{
    Services.Configure<AuthenticationOptions>(o =>
    {
        o.AddScheme(authenticationScheme, scheme => {
            scheme.HandlerType = typeof(THandler);
            scheme.DisplayName = displayName;
        });
    });
    if (configureOptions != null)
    {
        Services.Configure(authenticationScheme, configureOptions);
    }
    Services.AddTransient<THandler>();
    return this;
}

public class AuthenticationScheme
{
    public string Name { get; }
    public string DisplayName { get; }
    public Type HandlerType { get; }
}