1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00
Teknik/IdentityServer/Startup.cs
2021-07-12 20:21:50 -07:00

308 lines
12 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
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.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 Teknik.WebCommon.Middleware;
using Microsoft.Extensions.Hosting;
using Teknik.Middleware;
using Teknik.WebCommon;
using Teknik.IdentityServer.Controllers;
namespace Teknik.IdentityServer
{
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
string dataDir = (Configuration != null) ? Configuration["ConfigDirectory"] : null;
if (string.IsNullOrEmpty(dataDir))
{
string baseDir = Environment.ContentRootPath;
dataDir = Path.Combine(baseDir, "App_Data");
}
AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// Create Configuration Singleton
services.AddScoped<Config, Config>(opt => Config.Load(dataDir));
// Build an intermediate service provider
var sp = services.BuildServiceProvider();
// Resolve the services from the service provider
var config = sp.GetService<Config>();
var devEnv = config?.DevEnvironment ?? true;
var defaultConn = config?.DbConnection ?? string.Empty;
var authority = config?.UserConfig?.IdentityServerConfig?.Authority ?? string.Empty;
var signingCert = config?.UserConfig?.IdentityServerConfig?.SigningCertificate ?? string.Empty;
if (devEnv)
{
Environment.EnvironmentName = Environments.Development;
}
else
{
Environment.EnvironmentName = Environments.Production;
}
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "TeknikAuth";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
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
});
services.AddScoped<IErrorController, ErrorController>();
services.AddControllersWithViews()
.AddControllersAsServices()
.AddNewtonsoftJson();
// 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<ApplicationDbContext>(options => options
.UseLazyLoadingProxies()
.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)),
ServiceLifetime.Transient);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password = new PasswordOptions()
{
RequireDigit = false,
RequiredLength = 4,
RequiredUniqueChars = 1,
RequireLowercase = false,
RequireNonAlphanumeric = false,
RequireUppercase = false
};
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var identityBuilder = 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"));
options.Caching.ClientStoreExpiration = TimeSpan.FromHours(1);
})
.AddOperationalStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStoreCache()
.AddAspNetIdentity<ApplicationUser>()
.AddRedirectUriValidator<TeknikRedirectUriValidator>();
if (!string.IsNullOrEmpty(signingCert))
{
identityBuilder.AddSigningCredential($"CN={signingCert}");
}
else
{
identityBuilder.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 = authority;
options.RequireHttpsMetadata = true;
options.ApiName = "auth-api";
});
services.AddTransient<IPasswordHasher<ApplicationUser>, TeknikPasswordHasher>();
services.AddTransient<IProfileService, TeknikProfileService>();
}
public void Configure(IApplicationBuilder app, ApplicationDbContext dbContext, Config config)
{
// Create and Migrate the database
dbContext?.Database?.Migrate();
// Setup static files and cache them client side
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
}
});
// Initiate Routing
app.UseRouting();
// 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
}
});
// Force a HTTPS redirection (301)
app.UseHttpsRedirection();
// Use Exception Handling
app.UseErrorHandler();
// Custom Middleware
app.UseBlacklist();
app.UseCORS();
app.UseCSP();
app.UseSecurityHeaders();
// Cache Responses
app.UseResponseCaching();
if (config != null)
InitializeDbTestDataAsync(app, config);
app.UseIdentityServer();
// Authorize all the things!
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
private static void InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
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.ApiScopes.Any())
{
foreach (var apiScope in Resources.GetApiScopes())
{
context.ApiScopes.Add(apiScope.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in Resources.GetApiResources(config))
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
}
}