public static PasswordlessLoginBuilder AddOpenIdAuthority(this PasswordlessLoginBuilder builder, IConfiguration configuration)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            var hostingConfig = new HostingConfig();

            configuration.Bind(OpenIdAuthorityConstants.ConfigurationSections.Hosting, hostingConfig);
            builder.Services.AddSingleton(hostingConfig);

            var appConfigs = configuration.GetAppConfigs();
            var appService = new DefaultApplicationService(appConfigs);

            builder.Services.TryAddSingleton <IApplicationService>(appService);

            var clients  = AppConfigHelper.GetClientsFromAppConfig(appConfigs);
            var apps     = AppConfigHelper.GetAppsFromClients(clients);
            var appStore = new InMemoryAppStore(apps);

            builder.Services.TryAddSingleton <IAppStore>(appStore);

            var idScopeConfig = configuration.GetSection(OpenIdAuthorityConstants.ConfigurationSections.IdScopes).Get <List <IdScopeConfig> >() ?? new List <IdScopeConfig>();
            var idScopes      = idScopeConfig.Select(x => new IdentityResource(x.Name, x.IncludeClaimTypes)
            {
                Required = x.Required
            }).ToList();

            idScopes.AddRange(new List <IdentityResource>()
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email(),
                new IdentityResources.Phone(),
                new IdentityResources.Address(),
            });

            var apiConfigs   = configuration.GetSection(OpenIdAuthorityConstants.ConfigurationSections.Apis).Get <List <ApiConfig> >() ?? new List <ApiConfig>();
            var apiResources = apiConfigs.Select(x => new ApiResource(x.Url, x.IncludeClaimTypes)
            {
                ApiSecrets = x.Secrets?.ToList()?.Select(y => new Secret(y.Sha256())).ToList(),
                Scopes     = x.Scopes?.ToList()?.Select(y => new Scope(y)).ToList()
            }).ToList();

            builder.Services.AddTransient <ISignInService, OpenIdAuthoritySignInService>(); // NOTE: This must replace the service registered by PasswordlessLogin
            builder.Services.TryAddTransient <SimpleIAM.PasswordlessLogin.Services.Password.IReadOnlyPasswordService, SimpleIAM.PasswordlessLogin.Services.Password.DefaultPasswordService>();
            builder.Services.TryAddTransient <IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

            builder.Services.AddIdentityServer(options =>
            {
                options.UserInteraction.LoginUrl          = builder.Options.Urls.SignIn;
                options.UserInteraction.LogoutUrl         = builder.Options.Urls.SignOut;
                options.UserInteraction.LogoutIdParameter = OpenIdAuthorityConstants.Configuration.LogoutIdParameter;
                options.UserInteraction.ErrorUrl          = builder.Options.Urls.Error;
                options.Authentication.CookieLifetime     = TimeSpan.FromMinutes(builder.Options.DefaultSessionLengthMinutes);
            })
            .AddDeveloperSigningCredential()     //todo: replace
            .AddInMemoryApiResources(apiResources)
            .AddInMemoryClients(clients)
            .AddProfileService <ProfileService>()
            .AddInMemoryIdentityResources(idScopes);

            builder.Services.AddSingleton <IConfigureOptions <CookieAuthenticationOptions>, ReconfigureCookieOptions>();

            return(builder);
        }
        public static IServiceCollection AddOpenIdAuthority(this IServiceCollection services, IConfiguration configuration)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            var idProviderConfig = new IdProviderConfig();

            configuration.Bind("IdProvider", idProviderConfig);
            services.AddSingleton(idProviderConfig);

            var hostingConfig = new HostingConfig();

            configuration.Bind("Hosting", hostingConfig);
            services.AddSingleton(hostingConfig);

            var clientConfigs = configuration.GetSection("Apps").Get <List <ClientAppConfig> >() ?? new List <ClientAppConfig>();
            var clients       = ClientConfigHelper.GetClientsFromConfig(clientConfigs);
            var apps          = ClientConfigHelper.GetAppsFromClients(clients);
            var appStore      = new InMemoryAppStore(apps);

            services.AddSingleton <IAppStore>(appStore);

            var idScopeConfig = configuration.GetSection("IdScopes").Get <List <IdScopeConfig> >() ?? new List <IdScopeConfig>();
            var idScopes      = idScopeConfig.Select(x => new IdentityResource(x.Name, x.DisplayName ?? x.Name, x.ClaimTypes)
            {
                Required = x.Required
            }).ToList();

            idScopes.AddRange(new List <IdentityResource>()
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email(),
                new IdentityResources.Phone(),
                new IdentityResources.Address(),
            });

            var connection = configuration.GetConnectionString("OpenIdAuthority");

            services.AddDbContext <OpenIdAuthorityDbContext>(options => options.UseSqlServer(connection));
            services.AddTransient <IOneTimeCodeStore, DbOneTimeCodeStore>();
            services.AddTransient <IOneTimeCodeService, OneTimeCodeService>();
            services.AddTransient <IUserStore, DbUserStore>();
            services.AddTransient <IMessageService, MessageService>();

            services.AddIdentityServer(options =>
            {
                options.UserInteraction.LoginUrl          = "/signin";
                options.UserInteraction.LogoutUrl         = "/signout";
                options.UserInteraction.LogoutIdParameter = "id";
                options.UserInteraction.ErrorUrl          = "/error";
                options.Authentication.CookieLifetime     = TimeSpan.FromMinutes(idProviderConfig.DefaultSessionLengthMinutes);
            })
            .AddDeveloperSigningCredential()     //todo: replace
            .AddInMemoryClients(clients)
            .AddProfileService <ProfileService>()
            .AddInMemoryIdentityResources(idScopes);

            var smtpConfig = new SmtpConfig();

            configuration.Bind("Mail:Smtp", smtpConfig);
            services.AddSingleton(smtpConfig);
            services.AddTransient <IEmailService, SmtpEmailService>();

            var emailTemplates = ProcessEmailTemplates.GetTemplatesFromMailConfig(configuration.GetSection("Mail"));

            services.AddSingleton(emailTemplates);
            services.AddTransient <IEmailTemplateService, EmailTemplateService>();

            services.AddSingleton <IPasswordHashService>(new AspNetIdentityPasswordHashService(10000));
            services.AddTransient <IPasswordHashStore, DbPasswordHashStore>();
            services.AddTransient <IPasswordService, DefaultPasswordService>();

            services.AddTransient <AuthenticateOrchestrator>();
            services.AddTransient <UserOrchestrator>();

            services.AddEmbeddedViews();

            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            // IUrlHelper
            services.AddSingleton <IActionContextAccessor, ActionContextAccessor>();
            services.AddScoped <IUrlHelper>(x => {
                var actionContext = x.GetRequiredService <IActionContextAccessor>().ActionContext;
                var factory       = x.GetRequiredService <IUrlHelperFactory>();
                return(factory.GetUrlHelper(actionContext));
            });

            var allowedOrigins = clients.SelectMany(x => x.AllowedCorsOrigins).Distinct().ToArray();

            if (allowedOrigins.Length > 0)
            {
                services.AddCors(options =>
                {
                    options.AddPolicy("CorsPolicy", builder => builder
                                      .WithOrigins(allowedOrigins)
                                      .AllowAnyMethod()
                                      .AllowAnyHeader()
                                      .AllowCredentials());
                });
            }

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddSingleton <ITempDataProvider, CookieTempDataProvider>();

            return(services);
        }