using System; using System.IO; using System.Linq; using System.Reflection; using System.Security.Claims; using IdentityServer4.EntityFramework.DbContexts; using IdentityServer4.EntityFramework.Mappers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; using Teknik.Configuration; using Teknik.IdentityServer.Configuration; using Teknik.IdentityServer.Security; using Teknik.IdentityServer.Middleware; using Teknik.Logging; using Microsoft.AspNetCore.Authorization; using Teknik.IdentityServer.Models; using IdentityServer4.Services; using System.Collections.Generic; using Teknik.Utilities; namespace Teknik.IdentityServer { public class Startup { public Startup(IConfiguration configuration, IHostingEnvironment env) { Configuration = configuration; Environment = env; } public IConfiguration Configuration { get; } public IHostingEnvironment Environment { get; } public void ConfigureServices(IServiceCollection services) { string dataDir = Configuration["ConfigDirectory"]; AppDomain.CurrentDomain.SetData("DataDirectory", dataDir); var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; // Create Configuration Singleton services.AddScoped(opt => Config.Load(dataDir)); // Build an intermediate service provider var sp = services.BuildServiceProvider(); // Resolve the services from the service provider var config = sp.GetService(); if (config.DevEnvironment) { Environment.EnvironmentName = EnvironmentName.Development; } else { Environment.EnvironmentName = EnvironmentName.Production; } services.ConfigureApplicationCookie(options => { options.Cookie.Name = "TeknikAuth"; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict; options.Cookie.Expiration = TimeSpan.FromDays(30); options.ExpireTimeSpan = TimeSpan.FromDays(30); }); services.AddHttpsRedirection(options => { options.RedirectStatusCode = (Environment.IsDevelopment()) ? StatusCodes.Status307TemporaryRedirect : StatusCodes.Status308PermanentRedirect; #if DEBUG options.HttpsPort = 5050; #else options.HttpsPort = 443; #endif }); // Sessions services.AddResponseCaching(); services.AddMemoryCache(); services.AddSession(); // Set the anti-forgery cookie name services.AddAntiforgery(options => { options.Cookie.Name = "TeknikAuthAntiForgery"; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddDbContext(builder => builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))); services.AddIdentity(options => { options.Password = new PasswordOptions() { RequireDigit = false, RequiredLength = 4, RequiredUniqueChars = 1, RequireLowercase = false, RequireNonAlphanumeric = false, RequireUppercase = false }; }) .AddEntityFrameworkStores() .AddDefaultTokenProviders(); services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; options.UserInteraction.ErrorUrl = "/Error/IdentityError"; options.UserInteraction.ErrorIdParameter = "errorId"; options.Cors.CorsPaths.Add(new PathString("/connect/authorize")); options.Cors.CorsPaths.Add(new PathString("/connect/endsession")); options.Cors.CorsPaths.Add(new PathString("/connect/checksession")); options.Cors.CorsPaths.Add(new PathString("/connect/introspect")); }) .AddOperationalStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddConfigurationStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddAspNetIdentity() .AddRedirectUriValidator() .AddDeveloperSigningCredential(); services.AddAuthorization(options => { foreach (var policy in Policies.Get()) { options.AddPolicy(policy.Name, p => { foreach (var scope in policy.Scopes) { p.RequireScope(scope); } }); } }); services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = config.UserConfig.IdentityServerConfig.Authority; options.RequireHttpsMetadata = true; options.ApiName = "auth-api"; }); services.AddTransient, TeknikPasswordHasher>(); services.AddTransient(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Config config) { // Initiate Logging loggerFactory.AddLogger(config); // Setup the HttpContext app.UseHttpContextSetup(); // HttpContext Session app.UseSession(new SessionOptions() { IdleTimeout = TimeSpan.FromMinutes(30), Cookie = new CookieBuilder() { Name = "TeknikAuthSession", SecurePolicy = CookieSecurePolicy.Always, SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict } }); // Use Exception Handling app.UseErrorHandler(config); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // Custom Middleware app.UseBlacklist(); app.UseCORS(); app.UseCSP(); app.UseSecurityHeaders(); // Cache Responses app.UseResponseCaching(); // Force a HTTPS redirection (301) app.UseHttpsRedirection(); // Setup static files anc cache them client side app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => { ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000; } }); InitializeDbTestDataAsync(app, config).Wait(); app.UseIdentityServer(); app.UseMvcWithDefaultRoute(); } private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config) { using (var scope = app.ApplicationServices.GetService().CreateScope()) { scope.ServiceProvider.GetRequiredService().Database.Migrate(); scope.ServiceProvider.GetRequiredService().Database.Migrate(); scope.ServiceProvider.GetRequiredService().Database.Migrate(); var context = scope.ServiceProvider.GetRequiredService(); if (!context.Clients.Any()) { foreach (var client in Clients.Get(config)) { context.Clients.Add(client.ToEntity()); } context.SaveChanges(); } if (!context.IdentityResources.Any()) { foreach (var resource in Resources.GetIdentityResources()) { context.IdentityResources.Add(resource.ToEntity()); } context.SaveChanges(); } if (!context.ApiResources.Any()) { foreach (var resource in Resources.GetApiResources(config)) { context.ApiResources.Add(resource.ToEntity()); } context.SaveChanges(); } } } } }