using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using Bit.Core; using Bit.Core.Utilities; using AspNetCoreRateLimit; using System.Globalization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Logging; using System.IdentityModel.Tokens.Jwt; using System.Threading.Tasks; using IdentityServer4.Stores; using Bit.Core.IdentityServer; using IdentityServer4.Services; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using IdentityServer4.Extensions; namespace Bit.Identity { public class Startup { public Startup(IWebHostEnvironment env, IConfiguration configuration) { CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); Configuration = configuration; Environment = env; } public IConfiguration Configuration { get; private set; } public IWebHostEnvironment Environment { get; set; } public void ConfigureServices(IServiceCollection services) { // Options services.AddOptions(); // Settings var globalSettings = services.AddGlobalSettingsServices(Configuration); if (!globalSettings.SelfHosted) { services.Configure(Configuration.GetSection("IpRateLimitOptions")); services.Configure(Configuration.GetSection("IpRateLimitPolicies")); } // Data Protection services.AddCustomDataProtectionServices(Environment, globalSettings); // Repositories services.AddSqlServerRepositories(globalSettings); // Context services.AddScoped(); // Caching services.AddMemoryCache(); // Mvc services.AddMvc(); if (!globalSettings.SelfHosted) { // Rate limiting services.AddSingleton(); services.AddSingleton(); } // Cookies if (Environment.IsDevelopment()) { services.Configure(options => { options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified; options.OnAppendCookie = ctx => { ctx.CookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified; }; }); } JwtSecurityTokenHandler.DefaultMapInboundClaims = false; // Authentication services .AddAuthentication() .AddOpenIdConnect("sso", "Single Sign On", options => { options.Authority = globalSettings.BaseServiceUri.InternalSso; options.RequireHttpsMetadata = !Environment.IsDevelopment() && globalSettings.BaseServiceUri.InternalIdentity.StartsWith("https"); options.ClientId = "oidc-identity"; options.ClientSecret = globalSettings.OidcIdentityClientKey; options.SignInScheme = IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ResponseType = "code"; options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents { OnRedirectToIdentityProvider = context => { // Pass domain_hint onto the sso idp context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"]; if (context.Properties.Items.ContainsKey("user_identifier")) { context.ProtocolMessage.SessionState = context.Properties.Items["user_identifier"]; } return Task.FromResult(0); } }; }); // IdentityServer AddCustomIdentityServerServices(services, Environment, globalSettings); // Identity services.AddCustomIdentityServices(globalSettings); // Services services.AddBaseServices(); services.AddDefaultServices(globalSettings); services.AddCoreLocalizationServices(); if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName)) { services.AddHostedService(); } // HttpClients services.AddHttpClient("InternalSso", client => { client.BaseAddress = new Uri(globalSettings.BaseServiceUri.InternalSso); }); } public void Configure( IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime appLifetime, GlobalSettings globalSettings, ILogger logger) { IdentityModelEventSource.ShowPII = true; app.UseSerilog(env, appLifetime, globalSettings); if (globalSettings.SelfHosted) { app.UsePathBase("/identity"); app.UseForwardedHeaders(globalSettings); } // Default Middleware app.UseDefaultMiddleware(env, globalSettings); if (!globalSettings.SelfHosted) { // Rate limiting app.UseMiddleware(); } if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseCookiePolicy(); } // Add localization app.UseCoreLocalization(); // Add static files to the request pipeline. app.UseStaticFiles(); // Add routing app.UseRouting(); // Add Cors app.UseCors(policy => policy.SetIsOriginAllowed(o => CoreHelpers.IsCorsOriginAllowed(o, globalSettings)) .AllowAnyMethod().AllowAnyHeader().AllowCredentials()); // Add current context app.UseMiddleware(); // Add IdentityServer to the request pipeline. app.UseIdentityServer(); // Add Mvc stuff app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); // Log startup logger.LogInformation(Constants.BypassFiltersEventId, globalSettings.ProjectName + " started."); } public static IIdentityServerBuilder AddCustomIdentityServerServices(IServiceCollection services, IWebHostEnvironment env, GlobalSettings globalSettings) { services.AddSingleton(); services.AddTransient(); var issuerUri = new Uri(globalSettings.BaseServiceUri.InternalIdentity); var identityServerBuilder = services .AddIdentityServer(options => { options.Endpoints.EnableIntrospectionEndpoint = false; options.Endpoints.EnableEndSessionEndpoint = false; options.Endpoints.EnableUserInfoEndpoint = false; options.Endpoints.EnableCheckSessionEndpoint = false; options.Endpoints.EnableTokenRevocationEndpoint = false; options.IssuerUri = $"{issuerUri.Scheme}://{issuerUri.Host}"; options.Caching.ClientStoreExpiration = new TimeSpan(0, 5, 0); if (env.IsDevelopment()) { options.Authentication.CookieSameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified; } }) .AddInMemoryCaching() .AddInMemoryApiResources(ApiResources.GetApiResources()) .AddInMemoryApiScopes(ApiScopes.GetApiScopes()) .AddClientStoreCache() .AddCustomTokenRequestValidator() .AddProfileService() .AddResourceOwnerValidator() .AddPersistedGrantStore() .AddClientStore() .AddIdentityServerCertificate(env, globalSettings); services.AddTransient(); return identityServerBuilder; } } }