public static WebApplication ConfigureServices(this WebApplicationBuilder builder) { var env = builder.Environment; if (env.IsDevelopment()) { _logger.Info("Startup: Running in Development mode."); } else { _logger.Info("Startup: Running in Production mode."); } var configuration = builder.Configuration; var services = builder.Services; IClock clock = NodaTime.SystemClock.Instance; var rdsInstanceName = configuration["AWS:rdsInstanceName"]; var awsRegion = configuration["AWS:region"]; var awsBucket = configuration["AWS:bucket"]; var mailgunAPIKey = configuration["Mailgun:apiKey"]; var baseAddress = configuration["BaseAddress"]; var jwtSecret = configuration["Authentication:JWTSecret"]; var duendeLicense = configuration["IdentityServer:License"]; var identityConfig = new IdentityConfig(baseAddress, configuration["IdentityServer:MainSecret"], configuration["IdentityServer:CertificateKey"]); // Add application services. services.AddHttpClient(); services.AddTransient <IClock>(factory => clock); //MySQL Repos string connectionString = configuration.GetConnectionString("DefaultConnection"); var userStore = new MySQLFantasyCriticUserStore(connectionString, clock); var roleStore = new MySQLFantasyCriticRoleStore(connectionString); services.AddScoped <IFantasyCriticUserStore>(factory => userStore); services.AddScoped <IFantasyCriticRoleStore>(factory => roleStore); services.AddScoped <RepositoryConfiguration>(factory => new RepositoryConfiguration(connectionString, clock)); services.AddScoped <IUserStore <FantasyCriticUser>, MySQLFantasyCriticUserStore>(factory => userStore); services.AddScoped <IRoleStore <FantasyCriticRole>, MySQLFantasyCriticRoleStore>(factory => roleStore); services.AddScoped <IMasterGameRepo>(factory => new MySQLMasterGameRepo(connectionString, userStore)); services.AddScoped <IFantasyCriticRepo>(factory => new MySQLFantasyCriticRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore))); services.AddScoped <IRoyaleRepo>(factory => new MySQLRoyaleRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore), new MySQLFantasyCriticRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore)))); services.AddScoped <PatreonService>(factory => new PatreonService( configuration["PatreonService:AccessToken"], configuration["PatreonService:RefreshToken"], configuration["Authentication:Patreon:ClientId"], configuration["PatreonService:CampaignID"] )); services.AddScoped <EmailSendingServiceConfiguration>(_ => new EmailSendingServiceConfiguration(baseAddress, env.IsProduction())); services.AddScoped <IHypeFactorService>(factory => new LambdaHypeFactorService(awsRegion, awsBucket)); services.AddScoped <IRDSManager>(factory => new RDSManager(rdsInstanceName)); services.AddScoped <FantasyCriticUserManager>(); services.AddScoped <FantasyCriticRoleManager>(); services.AddScoped <GameAcquisitionService>(); services.AddScoped <LeagueMemberService>(); services.AddScoped <PublisherService>(); services.AddScoped <InterLeagueService>(); services.AddScoped <DraftService>(); services.AddScoped <GameSearchingService>(); services.AddScoped <ActionProcessingService>(); services.AddScoped <FantasyCriticService>(); services.AddScoped <RoyaleService>(); services.AddScoped <EmailSendingService>(); services.AddScoped <IEmailSender>(factory => new MailGunEmailSender("fantasycritic.games", mailgunAPIKey, "*****@*****.**", "Fantasy Critic")); AdminServiceConfiguration adminServiceConfiguration = new AdminServiceConfiguration(true); if (env.IsProduction() || env.IsStaging()) { adminServiceConfiguration = new AdminServiceConfiguration(false); } services.AddScoped <AdminServiceConfiguration>(_ => adminServiceConfiguration); services.AddScoped <AdminService>(); services.AddHttpClient <IOpenCriticService, OpenCriticService>(client => { client.BaseAddress = new Uri("https://api.opencritic.com/api/"); }); services.AddHttpClient <IGGService, GGService>(client => { client.BaseAddress = new Uri("https://api.ggapp.io/"); }); //Add scheduled tasks & scheduler services.AddSingleton <IScheduledTask, RefreshDataTask>(); services.AddSingleton <IScheduledTask, TimeFlagsTask>(); services.AddSingleton <IScheduledTask, PatreonUpdateTask>(); services.AddSingleton <IScheduledTask, EmailSendingTask>(); services.AddScheduler((sender, args) => { args.SetObserved(); }); services.AddIdentity <FantasyCriticUser, FantasyCriticRole>(options => { options.SignIn.RequireConfirmedAccount = false; var letters = "abcdefghijklmnopqrstuvwxyz"; var numbers = "0123456789"; var specials = "-._@+ "; options.User.AllowedUserNameCharacters = letters + letters.ToUpper() + numbers + specials; }) .AddUserManager <FantasyCriticUserManager>() .AddRoleManager <FantasyCriticRoleManager>() .AddDefaultTokenProviders(); var identityServerBuilder = services.AddIdentityServer(options => { options.LicenseKey = duendeLicense; options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; // see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/ options.EmitStaticAudienceClaim = true; }) .AddPersistedGrantStore <MySQLPersistedGrantStore>() .AddInMemoryIdentityResources(IdentityConfig.IdentityResources) .AddInMemoryApiScopes(IdentityConfig.APIScopes) .AddInMemoryApiResources(IdentityConfig.APIResources) .AddInMemoryClients(identityConfig.Clients) .AddAspNetIdentity <FantasyCriticUser>(); services.AddLocalApiAuthentication(); if (env.IsDevelopment()) { identityServerBuilder.AddDeveloperSigningCredential(); } else { identityServerBuilder.AddSigningCredential($"CN={identityConfig.KeyName}"); } services.AddAuthentication() .AddCookie(options => { options.Cookie.Name = "FantasyCriticCookie"; options.LoginPath = "/Identity/Account/Login"; options.LogoutPath = "/Identity/Account/Logout"; options.ExpireTimeSpan = TimeSpan.FromDays(30); options.SlidingExpiration = true; // the cookie would be re-issued on any request half way through the ExpireTimeSpan }) .AddGoogle(options => { options.ClientId = configuration["Authentication:Google:ClientId"]; options.ClientSecret = configuration["Authentication:Google:ClientSecret"]; }) .AddMicrosoftAccount(microsoftOptions => { microsoftOptions.AuthorizationEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"; microsoftOptions.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; microsoftOptions.ClientId = configuration["Authentication:Microsoft:ClientId"]; microsoftOptions.ClientSecret = configuration["Authentication:Microsoft:ClientSecret"]; }) .AddTwitch(options => { options.ClientId = configuration["Authentication:Twitch:ClientId"]; options.ClientSecret = configuration["Authentication:Twitch:ClientSecret"]; }) .AddPatreon(options => { options.ClientId = configuration["Authentication:Patreon:ClientId"]; options.ClientSecret = configuration["Authentication:Patreon:ClientSecret"]; }) .AddDiscord(options => { options.ClientId = configuration["Authentication:Discord:ClientId"]; options.ClientSecret = configuration["Authentication:Discord:ClientSecret"]; }); builder.Services.ConfigureApplicationCookie(options => { options.Events = SPACookieOptions.GetModifiedEvents(options.Events); }); services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(@"C:\FantasyCritic\Keys\")); services.AddHsts(options => { options.Preload = true; options.IncludeSubDomains = true; options.MaxAge = TimeSpan.FromDays(60); }); services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect; options.HttpsPort = 443; }); services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); }); services.AddRazorPages(); services.AddSignalR(); // In production, the Vue files will be served from this directory services.AddSpaStaticFiles(staticFileOptions => { staticFileOptions.RootPath = GetSPAPath(env); }); return(builder.Build()); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { _logger.LogInformation("Initializing services."); int validMinutes = Convert.ToInt32(Configuration["Tokens:ValidMinutes"]); var keyString = Configuration["Tokens:Key"]; var issuer = Configuration["Tokens:Issuer"]; var audience = Configuration["Tokens:Audience"]; IClock clock = SystemClock.Instance; var rdsInstanceName = Configuration["AWS:rdsInstanceName"]; // Add application services. var tokenService = new TokenService(keyString, issuer, audience, validMinutes); SendGridEmailSender sendGridEmailSender = new SendGridEmailSender(); services.AddHttpClient(); //Repository Setup. Uncomment either the MySQL section OR the fake repo section. Not both. //MySQL Repos string connectionString = Configuration.GetConnectionString("DefaultConnection"); var userStore = new MySQLFantasyCriticUserStore(connectionString, clock); var roleStore = new MySQLFantasyCriticRoleStore(connectionString); services.AddScoped <IFantasyCriticUserStore>(factory => userStore); services.AddScoped <IFantasyCriticRoleStore>(factory => roleStore); services.AddScoped <IMasterGameRepo>(factory => new MySQLMasterGameRepo(connectionString, userStore)); services.AddScoped <IFantasyCriticRepo>(factory => new MySQLFantasyCriticRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore))); services.AddScoped <IRoyaleRepo>(factory => new MySQLRoyaleRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore), new MySQLFantasyCriticRepo(connectionString, userStore, new MySQLMasterGameRepo(connectionString, userStore)))); services.AddScoped <IUserStore <FantasyCriticUser> >(factory => userStore); services.AddScoped <IRoleStore <FantasyCriticRole> >(factory => roleStore); //Fake Repos (for testing without a database) //var userStore = new FakeFantasyCriticUserStore(clock); //var roleStore = new FakeFantasyCriticRoleStore(); //services.AddScoped<IFantasyCriticUserStore>(factory => userStore); //services.AddScoped<IFantasyCriticRoleStore>(factory => roleStore); //services.AddScoped<IMasterGameRepo>(factory => new FakeMasterGameRepo(userStore)); //services.AddScoped<IFantasyCriticRepo>(factory => new FakeFantasyCriticRepo(userStore, new FakeMasterGameRepo(userStore))); //services.AddScoped<IRoyaleRepo>(factory => new FakeRoyaleRepo(userStore, new FakeMasterGameRepo(userStore))); //services.AddScoped<IUserStore<FantasyCriticUser>>(factory => userStore); //services.AddScoped<IRoleStore<FantasyCriticRole>>(factory => roleStore); services.AddScoped <IRDSManager>(factory => new RDSManager(rdsInstanceName)); services.AddScoped <FantasyCriticUserManager>(); services.AddScoped <FantasyCriticRoleManager>(); services.AddScoped <GameAcquisitionService>(); services.AddScoped <LeagueMemberService>(); services.AddScoped <PublisherService>(); services.AddScoped <InterLeagueService>(); services.AddScoped <DraftService>(); services.AddScoped <GameSearchingService>(); services.AddScoped <ActionProcessingService>(); services.AddScoped <FantasyCriticService>(); services.AddScoped <RoyaleService>(); services.AddTransient <IEmailSender>(factory => sendGridEmailSender); services.AddTransient <ISMSSender, SMSSender>(); services.AddTransient <ITokenService>(factory => tokenService); services.AddTransient <IClock>(factory => clock); services.AddHttpClient <IOpenCriticService, OpenCriticService>(); services.AddHttpClient <IOpenCriticService, OpenCriticService>(client => { client.BaseAddress = new Uri("https://api.opencritic.com/api/"); }); services.AddScoped <AdminService>(); //Add scheduled tasks & scheduler services.AddSingleton <IScheduledTask, RefreshDataTask>(); services.AddScheduler((sender, args) => { _logger.LogError(args.Exception.Message); args.SetObserved(); }); services.AddIdentity <FantasyCriticUser, FantasyCriticRole>(options => { options.Password.RequireDigit = false; options.Password.RequiredLength = 8; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; }) .AddDefaultTokenProviders(); services.ConfigureApplicationCookie(opt => { opt.ExpireTimeSpan = TimeSpan.FromMinutes(validMinutes); opt.Events.OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, opt.Events.OnRedirectToAccessDenied); opt.Events.OnRedirectToLogin = ReplaceRedirector(HttpStatusCode.Unauthorized, opt.Events.OnRedirectToLogin); }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(cfg => { cfg.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = issuer, ValidAudience = audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(keyString)) }; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. cfg.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // If the request is for our hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/updatehub"))) { // Read the token out of the query string context.Token = accessToken; } return(Task.CompletedTask); } }; }); services.AddHsts(options => { options.Preload = true; options.IncludeSubDomains = true; options.MaxAge = TimeSpan.FromDays(60); }); services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect; options.HttpsPort = 443; }); // Add framework services. services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .AddJsonOptions(options => { options.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); }); services.AddSignalR(); }