using FluentValidation; using Gameboard.ShogiUI.Sockets.Extensions; using Gameboard.ShogiUI.Sockets.Managers; using Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers; using Gameboard.ShogiUI.Sockets.Repositories; using Gameboard.ShogiUI.Sockets.ServiceModels.Socket; using Gameboard.ShogiUI.Sockets.Services; using Gameboard.ShogiUI.Sockets.Services.RequestValidators; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Http; using Microsoft.Identity.Client; using Microsoft.Identity.Web; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace Gameboard.ShogiUI.Sockets { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton, JoinByCodeRequestValidator>(); services.AddSingleton, JoinGameRequestValidator>(); services.AddSingleton(); services.AddTransient(); services.AddSingleton(); services.AddHttpClient("couchdb", c => { var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("admin:admin")); c.DefaultRequestHeaders.Add("Accept", "application/json"); c.DefaultRequestHeaders.Add("Authorization", $"Basic {base64}"); var baseUrl = $"{Configuration["AppSettings:CouchDB:Url"]}/{Configuration["AppSettings:CouchDB:Database"]}/"; c.BaseAddress = new Uri(baseUrl); }); services .AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.Formatting = Formatting.Indented; options.SerializerSettings.ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true } }; options.SerializerSettings.Converters = new[] { new StringEnumConverter() }; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; }); services.AddAuthentication("CookieOrJwt") .AddPolicyScheme("CookieOrJwt", "Either cookie or jwt", options => { options.ForwardDefaultSelector = context => { var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false; return bearerAuth ? JwtBearerDefaults.AuthenticationScheme : CookieAuthenticationDefaults.AuthenticationScheme; }; }) .AddCookie(options => { options.Cookie.Name = "session-id"; options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None; options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always; options.SlidingExpiration = true; }) .AddMicrosoftIdentityWebApi(Configuration); services.AddSwaggerDocument(config => { config.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme { Type = NSwag.OpenApiSecuritySchemeType.OAuth2, Flow = NSwag.OpenApiOAuth2Flow.AccessCode, AuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", TokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token", Scopes = new Dictionary { { "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/access_as_user", "The scope" } }, Scheme = "Bearer" }); config.PostProcess = document => { document.Info.Title = "Gameboard.ShogiUI.Sockets"; }; }); // Remove default HttpClient logging. services.RemoveAll(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISocketService socketConnectionManager) { var origins = new[] { "http://localhost:3000", "https://localhost:3000", "http://127.0.0.1:3000", "https://127.0.0.1:3000", "https://dev.lucaserver.space", "https://lucaserver.space" }; var socketOptions = new WebSocketOptions(); foreach (var o in origins) socketOptions.AllowedOrigins.Add(o); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); var client = PublicClientApplicationBuilder .Create(Configuration["AzureAd:ClientId"]) .WithLogging( (level, message, pii) => { }, LogLevel.Verbose, true, true ) .Build(); } else { app.UseHsts(); } app .UseRequestResponseLogging() .UseCors(opt => opt.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Set-Cookie").AllowCredentials()) .UseRouting() .UseAuthentication() .UseAuthorization() .UseOpenApi() .UseSwaggerUi3(config => { config.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings() { ClientId = "c1e94676-cab0-42ba-8b6c-9532b8486fff", UsePkceWithAuthorizationCodeGrant = true }; //config.WithCredentials = true; }) .UseWebSockets(socketOptions) .UseEndpoints(endpoints => { endpoints.MapControllers(); }) .Use(async (context, next) => { if (context.WebSockets.IsWebSocketRequest) { await socketConnectionManager.HandleSocketRequest(context); } else { await next(); } }); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.Indented, ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true } }, Converters = new[] { new StringEnumConverter() }, NullValueHandling = NullValueHandling.Ignore, }; } } }