1.3AuthenticationSchemeProvider--【AuthenticationSchemeProvider->IOptions<AuthenticationOptions>->AuthenticationScheme】
IAuthenticationSchemeProvider->AuthenticationSchemeProvider->IOptions<AuthenticationOptions>
AuthenticationScheme
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Authentication { /// <summary> /// Responsible for managing what authenticationSchemes are supported. /// </summary> public interface IAuthenticationSchemeProvider { /// <summary> /// Returns all currently registered <see cref="AuthenticationScheme"/>s. /// </summary> /// <returns>All currently registered <see cref="AuthenticationScheme"/>s.</returns> Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync(); /// <summary> /// Returns the <see cref="AuthenticationScheme"/> matching the name, or null. /// </summary> /// <param name="name">The name of the authenticationScheme.</param> /// <returns>The scheme or null if not found.</returns> Task<AuthenticationScheme?> GetSchemeAsync(string name); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticateScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns> Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultForbidScheme"/>. /// Otherwise, this will fallback to <see cref="GetDefaultChallengeSchemeAsync"/> . /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns> Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultSignOutScheme"/>. /// Otherwise, this will fallback to <see cref="GetDefaultSignInSchemeAsync"/> . /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync(); /// <summary> /// Registers a scheme for use by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="scheme">The scheme.</param> void AddScheme(AuthenticationScheme scheme); /// <summary> /// Registers a scheme for use by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="scheme">The scheme.</param> /// <returns>true if the scheme was added successfully.</returns> bool TryAddScheme(AuthenticationScheme scheme) { try { AddScheme(scheme); return true; } catch { return false; } } /// <summary> /// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="name">The name of the authenticationScheme being removed.</param> void RemoveScheme(string name); /// <summary> /// Returns the schemes in priority order for request handling. /// </summary> /// <returns>The schemes in priority order for request handling</returns> Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Authentication { /// <summary> /// Implements <see cref="IAuthenticationSchemeProvider"/>. /// </summary> public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider { /// <summary> /// Creates an instance of <see cref="AuthenticationSchemeProvider"/> /// using the specified <paramref name="options"/>, /// </summary> /// <param name="options">The <see cref="AuthenticationOptions"/> options.</param> public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options) : this(options, new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal)) { } /// <summary> /// Creates an instance of <see cref="AuthenticationSchemeProvider"/> /// using the specified <paramref name="options"/> and <paramref name="schemes"/>. /// </summary> /// <param name="options">The <see cref="AuthenticationOptions"/> options.</param> /// <param name="schemes">The dictionary used to store authentication schemes.</param> 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 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>(); private Task<AuthenticationScheme?> GetDefaultSchemeAsync() => _options.DefaultScheme != null ? GetSchemeAsync(_options.DefaultScheme) : Task.FromResult<AuthenticationScheme?>(null); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticateScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns> public virtual Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync() => _options.DefaultAuthenticateScheme != null ? GetSchemeAsync(_options.DefaultAuthenticateScheme) : GetDefaultSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> public virtual Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync() => _options.DefaultChallengeScheme != null ? GetSchemeAsync(_options.DefaultChallengeScheme) : GetDefaultSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultForbidScheme"/>. /// Otherwise, this will fallback to <see cref="GetDefaultChallengeSchemeAsync"/> . /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> public virtual Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync() => _options.DefaultForbidScheme != null ? GetSchemeAsync(_options.DefaultForbidScheme) : GetDefaultChallengeSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>. /// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns> public virtual Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync() => _options.DefaultSignInScheme != null ? GetSchemeAsync(_options.DefaultSignInScheme) : GetDefaultSchemeAsync(); /// <summary> /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultSignOutScheme"/>. /// Otherwise this will fallback to <see cref="GetDefaultSignInSchemeAsync"/> if that supports sign out. /// </summary> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.</returns> public virtual Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync() => _options.DefaultSignOutScheme != null ? GetSchemeAsync(_options.DefaultSignOutScheme) : GetDefaultSignInSchemeAsync(); /// <summary> /// Returns the <see cref="AuthenticationScheme"/> matching the name, or null. /// </summary> /// <param name="name">The name of the authenticationScheme.</param> /// <returns>The scheme or null if not found.</returns> public virtual Task<AuthenticationScheme?> GetSchemeAsync(string name) => Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null); /// <summary> /// Returns the schemes in priority order for request handling. /// </summary> /// <returns>The schemes in priority order for request handling</returns> public virtual Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync() => Task.FromResult(_requestHandlersCopy); /// <summary> /// Registers a scheme for use by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="scheme">The scheme.</param> /// <returns>true if the scheme was added successfully.</returns> public virtual bool TryAddScheme(AuthenticationScheme scheme) { if (_schemes.ContainsKey(scheme.Name)) { return false; } lock (_lock) { if (_schemes.ContainsKey(scheme.Name)) { return false; } if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType)) { _requestHandlers.Add(scheme); _requestHandlersCopy = _requestHandlers.ToArray(); } _schemes[scheme.Name] = scheme; _schemesCopy = _schemes.Values.ToArray(); return true; } } /// <summary> /// Registers a scheme for use by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="scheme">The scheme.</param> public virtual void AddScheme(AuthenticationScheme scheme) { if (_schemes.ContainsKey(scheme.Name)) { throw new InvalidOperationException("Scheme already exists: " + scheme.Name); } lock (_lock) { if (!TryAddScheme(scheme)) { throw new InvalidOperationException("Scheme already exists: " + scheme.Name); } } } /// <summary> /// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>. /// </summary> /// <param name="name">The name of the authenticationScheme being removed.</param> public virtual void RemoveScheme(string name) { if (!_schemes.ContainsKey(name)) { return; } lock (_lock) { if (_schemes.ContainsKey(name)) { var scheme = _schemes[name]; if (_requestHandlers.Remove(scheme)) { _requestHandlersCopy = _requestHandlers.ToArray(); } _schemes.Remove(name); _schemesCopy = _schemes.Values.ToArray(); } } } /// <inheritdoc /> public virtual Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync() => Task.FromResult(_schemesCopy); } }
using System; using System.Diagnostics.CodeAnalysis; namespace Microsoft.AspNetCore.Authentication { /// <summary> /// AuthenticationSchemes assign a name to a specific <see cref="IAuthenticationHandler"/> /// handlerType. /// </summary> public class AuthenticationScheme { /// <summary> /// Initializes a new instance of <see cref="AuthenticationScheme"/>. /// </summary> /// <param name="name">The name for the authentication scheme.</param> /// <param name="displayName">The display name for the authentication scheme.</param> /// <param name="handlerType">The <see cref="IAuthenticationHandler"/> type that handles this scheme.</param> public AuthenticationScheme(string name, string? displayName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type handlerType) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (handlerType == null) { throw new ArgumentNullException(nameof(handlerType)); } if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType)) { throw new ArgumentException("handlerType must implement IAuthenticationHandler."); } Name = name; HandlerType = handlerType; DisplayName = displayName; } /// <summary> /// The name of the authentication scheme. /// </summary> public string Name { get; } /// <summary> /// The display name for the scheme. Null is valid and used for non user facing schemes. /// </summary> public string? DisplayName { get; } /// <summary> /// The <see cref="IAuthenticationHandler"/> type that handles this scheme. /// </summary> [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public Type HandlerType { get; } } }
using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Security.Claims; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Authentication { /// <summary> /// Options to configure authentication. /// </summary> public class AuthenticationOptions { private readonly IList<AuthenticationSchemeBuilder> _schemes = new List<AuthenticationSchemeBuilder>(); /// <summary> /// Returns the schemes in the order they were added (important for request handling priority) /// </summary> public IEnumerable<AuthenticationSchemeBuilder> Schemes => _schemes; /// <summary> /// Maps schemes by name. /// </summary> public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = new Dictionary<string, AuthenticationSchemeBuilder>(StringComparer.Ordinal); /// <summary> /// Adds an <see cref="AuthenticationScheme"/>. /// </summary> /// <param name="name">The name of the scheme being added.</param> /// <param name="configureBuilder">Configures the scheme.</param> public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (configureBuilder == null) { throw new ArgumentNullException(nameof(configureBuilder)); } if (SchemeMap.ContainsKey(name)) { throw new InvalidOperationException("Scheme already exists: " + name); } var builder = new AuthenticationSchemeBuilder(name); configureBuilder(builder); _schemes.Add(builder); SchemeMap[name] = builder; } /// <summary> /// Adds an <see cref="AuthenticationScheme"/>. /// </summary> /// <typeparam name="THandler">The <see cref="IAuthenticationHandler"/> responsible for the scheme.</typeparam> /// <param name="name">The name of the scheme being added.</param> /// <param name="displayName">The display name for the scheme.</param> public void AddScheme<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]THandler>(string name, string? displayName) where THandler : IAuthenticationHandler => AddScheme(name, b => { b.DisplayName = displayName; b.HandlerType = typeof(THandler); }); /// <summary> /// Used as the fallback default scheme for all the other defaults. /// </summary> public string? DefaultScheme { get; set; } /// <summary> /// Used as the default scheme by <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>. /// </summary> public string? DefaultAuthenticateScheme { get; set; } /// <summary> /// Used as the default scheme by <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>. /// </summary> public string? DefaultSignInScheme { get; set; } /// <summary> /// Used as the default scheme by <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>. /// </summary> public string? DefaultSignOutScheme { get; set; } /// <summary> /// Used as the default scheme by <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>. /// </summary> public string? DefaultChallengeScheme { get; set; } /// <summary> /// Used as the default scheme by <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>. /// </summary> public string? DefaultForbidScheme { get; set; } /// <summary> /// If true, SignIn should throw if attempted with a user is not authenticated. /// A user is considered authenticated if <see cref="ClaimsIdentity.IsAuthenticated"/> returns <see langword="true" /> for the <see cref="ClaimsPrincipal"/> associated with the HTTP request. /// </summary> public bool RequireAuthenticatedSignIn { get; set; } = true; } }