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 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. else
  55. {
  56. Environment.EnvironmentName = EnvironmentName.Production;
  57. }
  58. services.ConfigureApplicationCookie(options =>
  59. {
  60. options.Cookie.Name = "TeknikAuth";
  61. options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  62. options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
  63. options.Cookie.Expiration = TimeSpan.FromDays(30);
  64. options.ExpireTimeSpan = TimeSpan.FromDays(30);
  65. });
  66. services.AddHttpsRedirection(options =>
  67. {
  68. options.RedirectStatusCode = (Environment.IsDevelopment()) ? StatusCodes.Status307TemporaryRedirect : StatusCodes.Status308PermanentRedirect;
  69. #if DEBUG
  70. options.HttpsPort = 5050;
  71. #else
  72. options.HttpsPort = 443;
  73. #endif
  74. });
  75. // Sessions
  76. services.AddResponseCaching();
  77. services.AddMemoryCache();
  78. services.AddSession();
  79. // Set the anti-forgery cookie name
  80. services.AddAntiforgery(options =>
  81. {
  82. options.Cookie.Name = "TeknikAuthAntiForgery";
  83. options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  84. options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
  85. });
  86. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  87. services.AddDbContext<ApplicationDbContext>(builder =>
  88. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)));
  89. services.AddIdentity<ApplicationUser, IdentityRole>(options =>
  90. {
  91. options.Password = new PasswordOptions()
  92. {
  93. RequireDigit = false,
  94. RequiredLength = 4,
  95. RequiredUniqueChars = 1,
  96. RequireLowercase = false,
  97. RequireNonAlphanumeric = false,
  98. RequireUppercase = false
  99. };
  100. })
  101. .AddEntityFrameworkStores<ApplicationDbContext>()
  102. .AddDefaultTokenProviders();
  103. services.AddIdentityServer(options =>
  104. {
  105. options.Events.RaiseErrorEvents = true;
  106. options.Events.RaiseInformationEvents = true;
  107. options.Events.RaiseFailureEvents = true;
  108. options.Events.RaiseSuccessEvents = true;
  109. options.UserInteraction.ErrorUrl = "/Error/IdentityError";
  110. options.UserInteraction.ErrorIdParameter = "errorId";
  111. options.Cors.CorsPaths.Add(new PathString("/connect/authorize"));
  112. options.Cors.CorsPaths.Add(new PathString("/connect/endsession"));
  113. options.Cors.CorsPaths.Add(new PathString("/connect/checksession"));
  114. options.Cors.CorsPaths.Add(new PathString("/connect/introspect"));
  115. })
  116. .AddOperationalStore(options =>
  117. options.ConfigureDbContext = builder =>
  118. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
  119. .AddConfigurationStore(options =>
  120. options.ConfigureDbContext = builder =>
  121. builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
  122. .AddAspNetIdentity<ApplicationUser>()
  123. .AddRedirectUriValidator<TeknikRedirectUriValidator>()
  124. .AddDeveloperSigningCredential();
  125. services.AddAuthorization(options =>
  126. {
  127. foreach (var policy in Policies.Get())
  128. {
  129. options.AddPolicy(policy.Name, p =>
  130. {
  131. foreach (var scope in policy.Scopes)
  132. {
  133. p.RequireScope(scope);
  134. }
  135. });
  136. }
  137. });
  138. services.AddAuthentication("Bearer")
  139. .AddIdentityServerAuthentication(options =>
  140. {
  141. options.Authority = config.UserConfig.IdentityServerConfig.Authority;
  142. options.RequireHttpsMetadata = true;
  143. options.ApiName = "auth-api";
  144. });
  145. services.AddTransient<IPasswordHasher<ApplicationUser>, TeknikPasswordHasher>();
  146. services.AddTransient<IProfileService, TeknikProfileService>();
  147. }
  148. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Config config)
  149. {
  150. // Initiate Logging
  151. loggerFactory.AddLogger(config);
  152. // Setup the HttpContext
  153. app.UseHttpContextSetup();
  154. // HttpContext Session
  155. app.UseSession(new SessionOptions()
  156. {
  157. IdleTimeout = TimeSpan.FromMinutes(30),
  158. Cookie = new CookieBuilder()
  159. {
  160. Name = "TeknikAuthSession",
  161. SecurePolicy = CookieSecurePolicy.Always,
  162. SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict
  163. }
  164. });
  165. // Use Exception Handling
  166. app.UseErrorHandler(config);
  167. if (env.IsDevelopment())
  168. {
  169. app.UseDeveloperExceptionPage();
  170. }
  171. // Custom Middleware
  172. app.UseBlacklist();
  173. app.UseCORS();
  174. app.UseCSP();
  175. app.UseSecurityHeaders();
  176. // Cache Responses
  177. app.UseResponseCaching();
  178. // Force a HTTPS redirection (301)
  179. app.UseHttpsRedirection();
  180. // Setup static files anc cache them client side
  181. app.UseStaticFiles(new StaticFileOptions
  182. {
  183. OnPrepareResponse = ctx =>
  184. {
  185. ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
  186. }
  187. });
  188. InitializeDbTestDataAsync(app, config).Wait();
  189. app.UseIdentityServer();
  190. app.UseMvcWithDefaultRoute();
  191. }
  192. private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
  193. {
  194. using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
  195. {
  196. scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
  197. scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
  198. scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
  199. var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
  200. if (!context.Clients.Any())
  201. {
  202. foreach (var client in Clients.Get(config))
  203. {
  204. context.Clients.Add(client.ToEntity());
  205. }
  206. context.SaveChanges();
  207. }
  208. if (!context.IdentityResources.Any())
  209. {
  210. foreach (var resource in Resources.GetIdentityResources())
  211. {
  212. context.IdentityResources.Add(resource.ToEntity());
  213. }
  214. context.SaveChanges();
  215. }
  216. if (!context.ApiResources.Any())
  217. {
  218. foreach (var resource in Resources.GetApiResources(config))
  219. {
  220. context.ApiResources.Add(resource.ToEntity());
  221. }
  222. context.SaveChanges();
  223. }
  224. }
  225. }
  226. }
  227. }