using System; using System.Text; using System.Threading.Tasks; using BlueWest.Cryptography; using BlueWest.Data; using BlueWest.WebApi.Context.Users; using BlueWest.WebApi.EF; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; namespace BlueWest.WebApi { /// /// Startup Extensions /// public static class StartupExtensions { private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build)); /// /// Get MYSQL Connection String /// /// /// /// private static DbContextOptionsBuilder GetMySqlSettings( this DbContextOptionsBuilder optionsBuilder, IConfiguration configuration, IWebHostEnvironment environment) { var sqlVersion = GetMySqlServerVersion(8, 0, 11); optionsBuilder .UseMySql( configuration.GetConnectionString("DockerMySQL"), sqlVersion) .UseMySql(sqlVersion, builder => { builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null); }); // The following three options help with debugging, but should // be changed or removed for production. if (environment.IsDevelopment()) { optionsBuilder .LogTo(Console.WriteLine, LogLevel.Information) .EnableSensitiveDataLogging() .EnableDetailedErrors(); } return optionsBuilder; } /// /// Setup database Contexts /// /// /// /// /// public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection, IConfiguration configuration, IWebHostEnvironment environment) { return serviceCollection .AddDbContextPool(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool(options => options.GetMySqlSettings(configuration, environment)); } /// /// Setup database Contexts /// /// /// /// /// public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection, IConfiguration configuration, IWebHostEnvironment environment) { var sqliteConString = "Data Source=BlueWest.Api.db"; return serviceCollection .AddDbContextPool(options => options.UseSqlite(sqliteConString)) .AddDbContextPool(options => options.UseSqlite(sqliteConString)) .AddDbContextPool(options => options.UseSqlite(sqliteConString)) .AddDbContextPool(options => options.UseSqlite(sqliteConString)); } public static void AddAuthServerServices(this IServiceCollection services, string origins, IConfiguration _configuration) { services.AddScoped(); services.AddScoped(); // User management services .AddIdentityCore(opt => { opt.User.RequireUniqueEmail = true; }) .AddEntityFrameworkStores() .AddUserManager() .AddUserStore(); // Database Context and Swagger services.TryAddSingleton(); // Registering 'services' and Authentication, Cookies, JWT services .AddScoped() .AddScoped() // So it gets successfully registered in UserManager .AddScoped() .AddScoped(); // Register the ConfigurationBuilder instance of AuthSettings var authSettings = _configuration.GetSection(nameof(AuthSettings)); services.Configure(authSettings); var signingKey = new SymmetricSecurityKey (Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)])); // jwt wire up // Get options from app settings var jwtAppSettingOptions = _configuration .GetSection(nameof(JwtIssuerOptions)); // Configure JwtIssuerOptions services.Configure(options => { options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)]; options.SigningCredentials = new SigningCredentials (signingKey, SecurityAlgorithms.HmacSha256); }); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)], ValidateAudience = true, ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)], ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, RequireExpirationTime = false, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddCookie(options => { options.LoginPath = "/api/auth/login2"; options.LogoutPath = "/api/auth/logout"; }) .AddJwtBearer(configureOptions => { configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; configureOptions.TokenValidationParameters = tokenValidationParameters; configureOptions.SaveToken = true; configureOptions.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return Task.CompletedTask; } }; }); // api user claim policy services.AddAuthorization(options => { options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol, Constants.JwtClaims.ApiAccess)); }); // add identity var identityBuilder = services.AddIdentityCore(o => { // configure identity options o.Password.RequireDigit = false; o.Password.RequireLowercase = false; o.Password.RequireUppercase = false; o.Password.RequireNonAlphanumeric = false; o.Password.RequiredLength = 6; }); identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(IdentityRole), identityBuilder.Services); identityBuilder.AddEntityFrameworkStores().AddDefaultTokenProviders(); } public static void ConfigureApiWithUsers(this IApplicationBuilder app, IWebHostEnvironment env, string origins) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseSwagger() .UseSwaggerUI(config => { config.SwaggerEndpoint("/swagger/v1/swagger.json", "Commands And Snippets API"); }) .UseRouting() .UseAuthentication() .UseAuthorization() .UseCors(origins) .UseEndpoints(endpoints => endpoints.MapControllers()); } } }