The next generation of the Teknik Services. Written in ASP.NET. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Startup.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Security.Claims;
  6. using IdentityServer4.EntityFramework.DbContexts;
  7. using IdentityServer4.EntityFramework.Mappers;
  8. using Microsoft.AspNetCore.Builder;
  9. using Microsoft.AspNetCore.Hosting;
  10. using Microsoft.AspNetCore.Http;
  11. using Microsoft.AspNetCore.Identity;
  12. using Microsoft.AspNetCore.Mvc;
  13. using Microsoft.EntityFrameworkCore;
  14. using Microsoft.Extensions.Configuration;
  15. using Microsoft.Extensions.DependencyInjection;
  16. using Microsoft.Extensions.Logging;
  17. using Microsoft.Net.Http.Headers;
  18. using Teknik.Configuration;
  19. using Teknik.IdentityServer.Configuration;
  20. using Teknik.IdentityServer.Security;
  21. using Teknik.IdentityServer.Middleware;
  22. using Teknik.Logging;
  23. using Microsoft.AspNetCore.Authorization;
  24. using Teknik.IdentityServer.Models;
  25. using IdentityServer4.Services;
  26. using System.Collections.Generic;
  27. using Teknik.Utilities;
  28. namespace Teknik.IdentityServer
  29. {
  30. public class Startup
  31. {
  32. public Startup(IConfiguration configuration, IHostingEnvironment env)
  33. {
  34. Configuration = configuration;
  35. Environment = env;
  36. }
  37. public IConfiguration Configuration { get; }
  38. public IHostingEnvironment Environment { get; }
  39. public void ConfigureServices(IServiceCollection services)
  40. {
  41. string dataDir = Configuration["ConfigDirectory"];
  42. AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
  43. var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
  44. // Create Configuration Singleton
  45. services.AddScoped<Config, Config>(opt => Config.Load(dataDir));
  46. // Build an intermediate service provider
  47. var sp = services.BuildServiceProvider();
  48. // Resolve the services from the service provider
  49. var config = sp.GetService<Config>();
  50. if (config.DevEnvironment)
  51. {
  52. Environment.EnvironmentName = EnvironmentName.Development;
  53. }
  54. services.ConfigureApplicationCookie(options =>
  55. {
  56. options.Cookie.Domain = CookieHelper.GenerateCookieDomain(config.UserConfig.IdentityServerConfig.Host, false, Environment.IsDevelopment());
  57. options.Cookie.Name = "TeknikAuth";
  58. options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  59. options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
  60. options.Cookie.Expiration = TimeSpan.FromDays(30);
  61. options.ExpireTimeSpan = TimeSpan.FromDays(30);
  62. });
  63. services.AddHttpsRedirection(options =>
  64. {
  65. options.RedirectStatusCode = (Environment.IsDevelopment()) ? StatusCodes.Status307TemporaryRedirect : StatusCodes.Status308PermanentRedirect;
  66. #if DEBUG
  67. options.HttpsPort = 5050;
  68. #else
  69. options.HttpsPort = 443;
  70. #endif
  71. });
  72. // Sessions
  73. services.AddResponseCaching();
  74. services.AddMemoryCache();
  75. services.AddSession();
  76. // Set the anti-forgery cookie name
  77. services.AddAntiforgery(options =>
  78. {
  79. options.Cookie.Domain = CookieHelper.GenerateCookieDomain(config.UserConfig.IdentityServerConfig.Host, false, Environment.IsDevelopment());
  80. options.Cookie.Name = "TeknikAuthAntiForgery";
  81. options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  82. options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
  83. });
  84. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  85. services.AddDbContext<ApplicationDbContext>(builder =>
  86. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)));
  87. services.AddIdentity<ApplicationUser, IdentityRole>(options =>
  88. {
  89. options.Password = new PasswordOptions()
  90. {
  91. RequireDigit = false,
  92. RequiredLength = 4,
  93. RequiredUniqueChars = 1,
  94. RequireLowercase = false,
  95. RequireNonAlphanumeric = false,
  96. RequireUppercase = false
  97. };
  98. })
  99. .AddEntityFrameworkStores<ApplicationDbContext>()
  100. .AddDefaultTokenProviders();
  101. services.AddIdentityServer(options =>
  102. {
  103. options.Events.RaiseErrorEvents = true;
  104. options.Events.RaiseInformationEvents = true;
  105. options.Events.RaiseFailureEvents = true;
  106. options.Events.RaiseSuccessEvents = true;
  107. options.UserInteraction.ErrorUrl = "/Error/IdentityError";
  108. options.UserInteraction.ErrorIdParameter = "errorId";
  109. options.Cors.CorsPaths.Add(new PathString("/connect/authorize"));
  110. options.Cors.CorsPaths.Add(new PathString("/connect/endsession"));
  111. options.Cors.CorsPaths.Add(new PathString("/connect/checksession"));
  112. options.Cors.CorsPaths.Add(new PathString("/connect/introspect"));
  113. })
  114. .AddOperationalStore(options =>
  115. options.ConfigureDbContext = builder =>
  116. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
  117. .AddConfigurationStore(options =>
  118. options.ConfigureDbContext = builder =>
  119. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
  120. .AddAspNetIdentity<ApplicationUser>()
  121. .AddRedirectUriValidator<TeknikRedirectUriValidator>()
  122. .AddDeveloperSigningCredential();
  123. services.AddAuthorization(options =>
  124. {
  125. foreach (var policy in Policies.Get())
  126. {
  127. options.AddPolicy(policy.Name, p =>
  128. {
  129. foreach (var scope in policy.Scopes)
  130. {
  131. p.RequireScope(scope);
  132. }
  133. });
  134. }
  135. });
  136. services.AddAuthentication("Bearer")
  137. .AddIdentityServerAuthentication(options =>
  138. {
  139. options.Authority = config.UserConfig.IdentityServerConfig.Authority;
  140. options.RequireHttpsMetadata = true;
  141. options.ApiName = "auth-api";
  142. });
  143. services.AddTransient<IPasswordHasher<ApplicationUser>, TeknikPasswordHasher>();
  144. services.AddTransient<IProfileService, TeknikProfileService>();
  145. }
  146. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Config config)
  147. {
  148. // Initiate Logging
  149. loggerFactory.AddLogger(config);
  150. // Setup the HttpContext
  151. app.UseHttpContextSetup();
  152. // HttpContext Session
  153. app.UseSession(new SessionOptions()
  154. {
  155. IdleTimeout = TimeSpan.FromMinutes(30),
  156. Cookie = new CookieBuilder()
  157. {
  158. Domain = CookieHelper.GenerateCookieDomain(config.UserConfig.IdentityServerConfig.Host, false, Environment.IsDevelopment()),
  159. Name = "TeknikAuthSession",
  160. SecurePolicy = CookieSecurePolicy.Always,
  161. SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict
  162. }
  163. });
  164. // Use Exception Handling
  165. app.UseErrorHandler(config);
  166. if (env.IsDevelopment())
  167. {
  168. app.UseDeveloperExceptionPage();
  169. }
  170. // Custom Middleware
  171. app.UseBlacklist();
  172. app.UseCORS();
  173. app.UseCSP();
  174. app.UseSecurityHeaders();
  175. // Cache Responses
  176. app.UseResponseCaching();
  177. // Force a HTTPS redirection (301)
  178. app.UseHttpsRedirection();
  179. // Setup static files anc cache them client side
  180. app.UseStaticFiles(new StaticFileOptions
  181. {
  182. OnPrepareResponse = ctx =>
  183. {
  184. ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
  185. }
  186. });
  187. InitializeDbTestDataAsync(app, config).Wait();
  188. app.UseIdentityServer();
  189. app.UseMvcWithDefaultRoute();
  190. }
  191. private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
  192. {
  193. using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
  194. {
  195. scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
  196. scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
  197. scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
  198. var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
  199. if (!context.Clients.Any())
  200. {
  201. foreach (var client in Clients.Get(config))
  202. {
  203. context.Clients.Add(client.ToEntity());
  204. }
  205. context.SaveChanges();
  206. }
  207. if (!context.IdentityResources.Any())
  208. {
  209. foreach (var resource in Resources.GetIdentityResources())
  210. {
  211. context.IdentityResources.Add(resource.ToEntity());
  212. }
  213. context.SaveChanges();
  214. }
  215. if (!context.ApiResources.Any())
  216. {
  217. foreach (var resource in Resources.GetApiResources(config))
  218. {
  219. context.ApiResources.Add(resource.ToEntity());
  220. }
  221. context.SaveChanges();
  222. }
  223. }
  224. }
  225. }
  226. }