/// <summary>
        /// Adds authentication services to the specified <see cref="IServiceCollection" />. 
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
        /// <returns>A reference to this instance after the operation has completed.</returns>
        public static IServiceCollection AddAuthentication(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddWebEncoders();
            services.AddDataProtection();
            return services;
        }
        public static SignalRServicesBuilder AddSignalR(this IServiceCollection services, Action<SignalROptions> configureOptions)
        {
            // Dependencies
            services.AddOptions();
            services.AddDataProtection();
            services.AddMessageBus();

            // SignalR services
            services.TryAddSingleton<IMemoryPool, MemoryPool>();
            services.TryAddSingleton<ITransportManager, TransportManager>();
            services.TryAddSingleton<ITransportHeartbeat, TransportHeartbeat>();
            services.TryAddSingleton<IConnectionManager, ConnectionManager>();
            services.TryAddSingleton<IAckHandler, AckHandler>();
            services.TryAddSingleton<AckSubscriber, AckSubscriber>();
            services.TryAddSingleton<IAssemblyLocator, DefaultAssemblyLocator>();
            services.TryAddSingleton<IHubManager, DefaultHubManager>();
            services.TryAddSingleton<IMethodDescriptorProvider, ReflectedMethodDescriptorProvider>();
            services.TryAddSingleton<IHubDescriptorProvider, ReflectedHubDescriptorProvider>();
            services.TryAddSingleton<JsonSerializer, JsonSerializer>();
            services.TryAddSingleton<IUserIdProvider, PrincipalUserIdProvider>();
            services.TryAddSingleton<IParameterResolver, DefaultParameterResolver>();
            services.TryAddSingleton<IHubActivator, DefaultHubActivator>();
            services.TryAddSingleton<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>();
            services.TryAddSingleton<IJavaScriptMinifier, NullJavaScriptMinifier>();
            services.TryAddSingleton<IHubRequestParser, HubRequestParser>();
            services.TryAddSingleton<IHubPipelineInvoker, HubPipeline>();

            services.TryAddSingleton(typeof(IPersistentConnectionContext<>), typeof(PersistentConnectionContextService<>));
            services.TryAddSingleton(typeof(IHubContext<>), typeof(HubContextService<>));
            services.TryAddSingleton(typeof(IHubContext<,>), typeof(HubContextService<,>));

            //allows detecting if services were registered
            services.TryAddSingleton<SignalRMarkerService, SignalRMarkerService>();

            // TODO: Just use the new IDataProtectionProvider abstraction directly here
            services.TryAddSingleton<IProtectedData, DataProtectionProviderProtectedData>();

            // Setup the default SignalR options
            services.TryAddTransient<IConfigureOptions<SignalROptions>, SignalROptionsSetup>();

            if (configureOptions != null)
            {
                services.Configure(configureOptions);
            }

            if (PlatformServices.Default?.LibraryManager != null)
            {
                services.TryAddSingleton(PlatformServices.Default.LibraryManager);
            }

            return new SignalRServicesBuilder(services);
        }
        /// <summary>
        /// Adds default Data Protection services to an <see cref="IServiceCollection"/> and configures the behavior of the Data Protection system.
        /// </summary>
        /// <param name="services">A service collection to which Data Protection has already been added.</param>
        /// <param name="configure">A callback which takes a <see cref="DataProtectionConfiguration"/> parameter.
        /// This callback will be responsible for configuring the system.</param>
        /// <returns>The <paramref name="services"/> instance.</returns>
        public static IServiceCollection AddDataProtection(this IServiceCollection services, Action<DataProtectionConfiguration> configure)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

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

            configure(new DataProtectionConfiguration(services));
            return services.AddDataProtection();
        }
        /// <summary>
        /// Adds data protection services to the specified <see cref="IServiceCollection" />.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
        /// <param name="setupAction">An <see cref="Action{DataProtectionOptions}"/> to configure the provided <see cref="DataProtectionOptions"/>.</param>
        /// <returns>A reference to this instance after the operation has completed.</returns>
        public static IDataProtectionBuilder AddDataProtection(this IServiceCollection services, Action<DataProtectionOptions> setupAction)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

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

            var builder = services.AddDataProtection();
            services.Configure(setupAction);
            return builder;
        }
        /// <summary>
        /// Adds default services to the service collection
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        private static IServiceCollection TryAddDefaultServices(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));
            services.AddLogging();
            services.AddDataProtection();

            var descriptor = new ServiceDescriptor(typeof(IAuthenticatorDataRepository), typeof(UserProfileAuthenticatorDataRepository), ServiceLifetime.Scoped);
            services.TryAdd(descriptor);

            descriptor = new ServiceDescriptor(typeof(IEnrollmentClient), typeof(EnrollmentClient), ServiceLifetime.Scoped);
            services.TryAdd(descriptor);

            descriptor = new ServiceDescriptor(typeof(IAuthenticator), typeof(Authenticator), ServiceLifetime.Scoped);
            services.TryAdd(descriptor);

            return services;
        }
        public static IServiceCollection AddAntiforgery(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddDataProtection();
            services.AddWebEncoders();

            // Don't overwrite any options setups that a user may have added.
            services.TryAddEnumerable(
                ServiceDescriptor.Transient<IConfigureOptions<AntiforgeryOptions>, AntiforgeryOptionsSetup>());

            services.TryAddSingleton<IAntiforgery, DefaultAntiforgery>();
            services.TryAddSingleton<IAntiforgeryTokenGenerator, DefaultAntiforgeryTokenGenerator>();
            services.TryAddSingleton<IAntiforgeryTokenSerializer, DefaultAntiforgeryTokenSerializer>();
            services.TryAddSingleton<IAntiforgeryTokenStore, DefaultAntiforgeryTokenStore>();
            services.TryAddSingleton<IClaimUidExtractor, DefaultClaimUidExtractor>();
            services.TryAddScoped<IAntiforgeryContextAccessor, DefaultAntiforgeryContextAccessor>();
            services.TryAddSingleton<IAntiforgeryAdditionalDataProvider, DefaultAntiforgeryAdditionalDataProvider>();
            return services;
        }