/// <summary>
        /// This method gets called by the runtime.
        /// Use this method to configure the HTTP request pipeline.
        /// </summary>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // HttpContextのマイグレーション用
            app._UseHttpContextAccessor();

            app.UseStaticFiles();
            app.UseHsts();

            // 内部通信をHTTPS→HTTPにするので、コメントアウト。
            //app.UseHttpsRedirection();

            app.UseDeveloperExceptionPage();
            //app.UseExceptionHandler("/Home/Error");

            // Cookieを使用する。
            app.UseCookiePolicy(new CookiePolicyOptions()
            {
                HttpOnly = HttpOnlyPolicy.Always,
                // https://github.com/aspnet/Security/issues/1822
                MinimumSameSitePolicy = SameSiteMode.None, //SameSiteMode.Strict,
                //Secure= CookieSecurePolicy.Always
            });

            // Sessionを使用する。
            app.UseSession(new SessionOptions()
            {
                IdleTimeout = TimeSpan.FromMinutes(30), // ここで調整
                IOTimeout   = TimeSpan.FromSeconds(30),
                Cookie      = new CookieBuilder()
                {
                    Expiration   = TimeSpan.FromDays(1), // 効かない
                    HttpOnly     = true,
                    Name         = GetConfigParameter.GetAnyConfigValue("sessionState:SessionCookieName"),
                    Path         = "/",
                    SameSite     = SameSiteMode.Strict,
                    SecurePolicy = CookieSecurePolicy.SameAsRequest
                }
            });

            // Routing
            app.UseRouting();

            // Identity
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseCors( //認証・認可の後ろ
                builder => builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            //.AllowCredentials());

            app.UseEndpoints(endpoints =>
            {
                #region Account
                endpoints.MapControllerRoute(
                    name: "Saml2Request",
                    pattern: Config.Saml2RequestEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "Account", action = "Saml2Request" });

                endpoints.MapControllerRoute(
                    name: "OAuth2Authorize",
                    pattern: Config.OAuth2AuthorizeEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "Account", action = "OAuth2Authorize" });
                #endregion

                #region OAuth2Endpoint

                #region OAuth2 / OIDC

                endpoints.MapControllerRoute(
                    name: "OAuth2Token",
                    pattern: Config.OAuth2TokenEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "OAuth2Token" });

                endpoints.MapControllerRoute(
                    name: "GetUserClaims",
                    pattern: Config.OAuth2UserInfoEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "GetUserClaims" });

                endpoints.MapControllerRoute(
                    name: "RevokeToken",
                    pattern: Config.OAuth2RevokeTokenEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "RevokeToken" });

                endpoints.MapControllerRoute(
                    name: "IntrospectToken",
                    pattern: Config.OAuth2IntrospectTokenEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "IntrospectToken" });

                endpoints.MapControllerRoute(
                    name: "JwksUri",
                    pattern: OAuth2AndOIDCParams.JwkSetUri.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "JwksUri" });

                endpoints.MapControllerRoute(
                    name: "RequestObjectUri",
                    pattern: OAuth2AndOIDCParams.RequestObjectRegUri.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "RequestObjectUri" });

                #endregion

                #region CIBA FAPI2

                endpoints.MapControllerRoute(
                    name: "CibaAuthorize",
                    pattern: Config.CibaAuthorizeEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "CibaAuthorize" });

                endpoints.MapControllerRoute(
                    name: "CibaPushResult",
                    pattern: Config.CibaPushResultEndpoint.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "CibaPushResult" });

                #endregion

                #region Push Notification

                endpoints.MapControllerRoute(
                    name: "SetDeviceToken",
                    pattern: Config.SetDeviceTokenWebAPI.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "SetDeviceToken" });

                endpoints.MapControllerRoute(
                    name: "TwoFactorAuthPushResult",
                    pattern: Config.TwoFactorAuthPushResultWebAPI.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2Endpoint", action = "TwoFactorAuthPushResult" });

                #endregion

                #endregion

                #region OAuth2ResourceServer
                endpoints.MapControllerRoute(
                    name: "TestHybridFlow",
                    pattern: Config.TestHybridFlowWebAPI.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2ResourceServer", action = "TestHybridFlow" });

                endpoints.MapControllerRoute(
                    name: "ChageToUser",
                    pattern: Config.ChageToUserWebAPI.Substring(1), // 先頭の[/]を削除,
                    defaults: new { controller = "OAuth2ResourceServer", action = "ChageToUser" });
                #endregion

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
        /// <summary>
        /// This method gets called by the runtime.
        /// Use this method to add services to the container.
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        public void ConfigureServices(IServiceCollection services)
        {
            // 構成情報から、AppConfiguration SectionをAppConfiguration Classへバインドするようなケース。
            //services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));

            // カレント・ディレクトリを変更する。
            Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));

            // HttpContextのマイグレーション用
            services._AddHttpContextAccessor();

            services.Configure <CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent
                // for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
            });

            #region Redisはインフラ

            string redisConfig = Environment.GetEnvironmentVariable("RedisConfig");
            if (string.IsNullOrEmpty(redisConfig))
            {
                redisConfig = "localhost";
            }

            string redisInstanceName = Environment.GetEnvironmentVariable("RedisInstanceName");
            if (string.IsNullOrEmpty(redisInstanceName))
            {
                redisInstanceName = "redis";
            }

            ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConfig);
            #endregion

            // DataProtection
            services
            .AddDataProtection()
            .SetApplicationName(GetConfigParameter.GetAnyConfigValue("sessionState:ApplicationName"))
            .PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys");

            // Sessionのモード
            //services.AddDistributedMemoryCache();
            //services.AddDistributedSqlServerCache();
            services.AddStackExchangeRedisCache(option =>
            {
                option.Configuration = redisConfig;
                option.InstanceName  = redisInstanceName;
            });

            // Sessionを使用する。
            services.AddSession();

            // Core 3.0のテンプレートではUseMvcの
            // 代わりにこれらを使用するようになった。
            services
            .AddControllersWithViews() // MVC & WebAPI
            .AddNewtonsoftJson();      // JSON シリアライザの変更

            #region Add Frameworks

            // 一般的な Webアプリでは、
            // EF, Identity, MVC などのミドルウェア サービスを登録する。
            // ミドルウェアの実行順序は、IStartupFilter の登録順に設定される。

            // EF
            //services.AddDbContext<ApplicationDbContext>(options =>
            //    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            // AddMvc
            services.AddMvc();

            // AddCors
            services.AddCors(
                o => o.AddPolicy("AllowAllOrigins",
                                 builder =>
            {
                builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader();
            }));

            #region ASP.NET Core Identity

            // must be added before AddIdentity()
            services.AddScoped <IPasswordHasher <ApplicationUser>, CustomPasswordHasher <ApplicationUser> >();
            services.AddScoped <ISecurityStampValidator, SecurityStampValidator <ApplicationUser> >();

            services.AddIdentity <ApplicationUser, ApplicationRole>()
            //.AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserStore <UserStoreCore>()
            .AddRoleStore <RoleStoreCore>()
            .AddDefaultTokenProviders();

            // Add application services.
            services.AddTransient <IUserStore <ApplicationUser>, UserStoreCore>();
            services.AddTransient <IRoleStore <ApplicationRole>, RoleStoreCore>();
            services.AddTransient <IEmailSender, EmailSender>();
            services.AddTransient <ISmsSender, SmsSender>();

            #region 認証

            #region IdentityOptions

            Action <IdentityOptions> IdentityOptionsConf = new Action <IdentityOptions>(idOptions =>
            {
                // ユーザー
                // https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2#user
                //idOptions.SignIn.AllowedUserNameCharacters = false;
                idOptions.User.RequireUniqueEmail = Config.RequireUniqueEmail;

                // サインイン
                // https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2#sign-in
                idOptions.SignIn.RequireConfirmedEmail       = false;
                idOptions.SignIn.RequireConfirmedPhoneNumber = false;

                // パスワード検証(8文字以上の大文字・小文字、数値、記号
                // https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2#password
                idOptions.Password.RequiredLength         = Config.RequiredLength;
                idOptions.Password.RequireNonAlphanumeric = Config.RequireNonLetterOrDigit;
                idOptions.Password.RequireDigit           = Config.RequireDigit;
                idOptions.Password.RequireLowercase       = Config.RequireLowercase;
                idOptions.Password.RequireUppercase       = Config.RequireUppercase;

                // ユーザ ロックアウト
                // https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2#lockout
                idOptions.Lockout.DefaultLockoutTimeSpan  = Config.DefaultAccountLockoutTimeSpanFromSeconds;
                idOptions.Lockout.MaxFailedAccessAttempts = Config.MaxFailedAccessAttemptsBeforeLockout;
                idOptions.Lockout.AllowedForNewUsers      = Config.UserLockoutEnabledByDefault;

                // 二要素認証

                // トークン
                // https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2#tokens
                //idOptions.Tokens...
            });

            services.Configure <IdentityOptions>(IdentityOptionsConf);

            #endregion

            #region AuthOptions

            AuthenticationBuilder authenticationBuilder = services.AddAuthentication();

            #region AuthCookie

            authenticationBuilder.AddCookie(options =>
            {
                // https://community.auth0.com/t/asp-net-core-2-intermittent-correlation-failed-errors/11918/18
                options.LoginPath          = "/MultiPurposeAuthSite/Account/Login";
                options.LogoutPath         = "/MultiPurposeAuthSite/Account/LogOff";
                options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
                options.ExpireTimeSpan     = new TimeSpan(0, 2, 0);
                options.SlidingExpiration  = true;

                //options.AccessDeniedPath = "/Identity/Account/AccessDenied";
                options.Cookie.Name     = GetConfigParameter.GetAnyConfigValue("sessionState:ApplicationName") + "_AuthCookie";
                options.Cookie.HttpOnly = true;

                options.Events = options.Events = new CookieAuthenticationEvents()
                {
                    OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
                };
            });

            #endregion

            #region 外部ログイン
            if (Config.MicrosoftAccountAuthentication)
            {
                authenticationBuilder.AddMicrosoftAccount(options =>
                {
                    options.ClientId     = Config.MicrosoftAccountAuthenticationClientId;
                    options.ClientSecret = Config.MicrosoftAccountAuthenticationClientSecret;
                });
            }
            if (Config.GoogleAuthentication)
            {
                authenticationBuilder.AddGoogle(options =>
                {
                    options.ClientId     = Config.GoogleAuthenticationClientId;
                    options.ClientSecret = Config.GoogleAuthenticationClientSecret;
                });
            }
            if (Config.FacebookAuthentication)
            {
                authenticationBuilder.AddFacebook(options =>
                {
                    options.AppId     = Config.FacebookAuthenticationClientId;
                    options.AppSecret = Config.FacebookAuthenticationClientSecret;
                });
            }
            if (Config.TwitterAuthentication)
            {
                authenticationBuilder.AddTwitter(options =>
                {
                    options.ConsumerKey         = Config.TwitterAuthenticationClientId;
                    options.ConsumerSecret      = Config.TwitterAuthenticationClientSecret;
                    options.RetrieveUserDetails = true;
                });
            }
            #endregion

            #region OAuth2 / OIDC

            // スクラッチ実装

            #endregion

            #endregion

            services.Configure <SecurityStampValidatorOptions>(options =>
            {
                options.ValidationInterval = Config.SecurityStampValidateIntervalFromSeconds;
            });

            #endregion

            #endregion

            #region Forms認証
            //services.AddAuthentication(options =>
            //{
            //    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //})
            //.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            //{
            //    options.LoginPath = new PathString("/Home/Login");
            //    //options.LogoutPath = new PathString("/Home/Logout");
            //    options.AccessDeniedPath = new PathString(GetConfigParameter.GetConfigValue("FxErrorScreenPath"));
            //    options.ReturnUrlParameter = "ReturnUrl";
            //    options.ExpireTimeSpan = TimeSpan.FromHours(1);
            //    options.SlidingExpiration = true;
            //    options.Cookie.HttpOnly = true;
            //    //options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\artifacts"));
            //});
            #endregion

            #endregion
        }