223 lines
7.2 KiB
C#
223 lines
7.2 KiB
C#
using FluentValidation;
|
|
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.HttpLogging;
|
|
using Microsoft.Identity.Web;
|
|
using Microsoft.OpenApi.Models;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Converters;
|
|
using Newtonsoft.Json.Serialization;
|
|
using System.Text;
|
|
|
|
namespace Gameboard.ShogiUI.Sockets
|
|
{
|
|
public class Program
|
|
{
|
|
public static void Main(string[] args)
|
|
{
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
ConfigureAuthentication(builder);
|
|
ConfigureControllersWithNewtonsoft(builder);
|
|
ConfigureSwagger(builder);
|
|
ConfigureDependencyInjection(builder);
|
|
ConfigureLogging(builder);
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseHttpLogging();
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI(options =>
|
|
{
|
|
options.OAuthScopes("api://c1e94676-cab0-42ba-8b6c-9532b8486fff/DefaultScope");
|
|
options.OAuthConfigObject.ClientId = builder.Configuration["AzureAd:SwaggerUIClientId"];
|
|
options.OAuthConfigObject.UsePkceWithAuthorizationCodeGrant = true;
|
|
});
|
|
app.UseHttpsRedirection(); // Apache handles HTTPS in production.
|
|
}
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapControllers();
|
|
UseCorsAndWebSockets(app);
|
|
|
|
app.Run();
|
|
}
|
|
|
|
private static void UseCorsAndWebSockets(WebApplication app)
|
|
{
|
|
// TODO: Figure out how to make a middleware for sockets?
|
|
var socketService = app.Services.GetRequiredService<ISocketService>();
|
|
var allowedOrigins = app.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>();
|
|
|
|
var socketOptions = new WebSocketOptions();
|
|
foreach (var origin in allowedOrigins)
|
|
socketOptions.AllowedOrigins.Add(origin);
|
|
|
|
app.UseCors(options =>
|
|
{
|
|
options
|
|
.WithOrigins(allowedOrigins)
|
|
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
.AllowAnyMethod()
|
|
.AllowAnyHeader()
|
|
.WithExposedHeaders("Set-Cookie")
|
|
.AllowCredentials();
|
|
});
|
|
app.UseWebSockets(socketOptions);
|
|
app.Use(async (context, next) =>
|
|
{
|
|
Console.WriteLine("Use websocket");
|
|
if (context.WebSockets.IsWebSocketRequest)
|
|
{
|
|
Console.WriteLine("Is websocket request");
|
|
await socketService.HandleSocketRequest(context);
|
|
}
|
|
else
|
|
{
|
|
await next();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void ConfigureLogging(WebApplicationBuilder builder)
|
|
{
|
|
builder.Services.AddHttpLogging(options =>
|
|
{
|
|
options.LoggingFields = HttpLoggingFields.Request;
|
|
});
|
|
}
|
|
|
|
private static void ConfigureAuthentication(WebApplicationBuilder builder)
|
|
{
|
|
// Add services to the container.
|
|
builder.Services
|
|
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
|
|
|
|
builder.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 = SameSiteMode.None;
|
|
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
|
options.SlidingExpiration = true;
|
|
});
|
|
}
|
|
|
|
private static void ConfigureControllersWithNewtonsoft(WebApplicationBuilder builder)
|
|
{
|
|
builder.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;
|
|
});
|
|
|
|
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
|
{
|
|
Formatting = Formatting.Indented,
|
|
ContractResolver = new DefaultContractResolver
|
|
{
|
|
NamingStrategy = new CamelCaseNamingStrategy
|
|
{
|
|
ProcessDictionaryKeys = true
|
|
}
|
|
},
|
|
Converters = new[] { new StringEnumConverter() },
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
};
|
|
}
|
|
|
|
private static void ConfigureDependencyInjection(WebApplicationBuilder builder)
|
|
{
|
|
var services = builder.Services;
|
|
services.AddSingleton<IJoinByCodeHandler, JoinByCodeHandler>();
|
|
services.AddSingleton<ISocketConnectionManager, SocketConnectionManager>();
|
|
services.AddSingleton<ISocketTokenCache, SocketTokenCache>();
|
|
services.AddSingleton<IGameboardManager, GameboardManager>();
|
|
services.AddSingleton<IValidator<JoinByCodeRequest>, JoinByCodeRequestValidator>();
|
|
services.AddSingleton<IValidator<JoinGameRequest>, JoinGameRequestValidator>();
|
|
services.AddSingleton<ISocketService, SocketService>();
|
|
services.AddTransient<IGameboardRepository, GameboardRepository>();
|
|
services.AddTransient<IClaimsTransformation, ShogiUserClaimsTransformer>();
|
|
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 = $"{builder.Configuration["AppSettings:CouchDB:Url"]}/{builder.Configuration["AppSettings:CouchDB:Database"]}/";
|
|
c.BaseAddress = new Uri(baseUrl);
|
|
});
|
|
services.AddTransient<IModelMapper, ModelMapper>();
|
|
}
|
|
|
|
private static void ConfigureSwagger(WebApplicationBuilder builder)
|
|
{
|
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen(options =>
|
|
{
|
|
var bearerKey = "Bearer";
|
|
options.AddSecurityDefinition(bearerKey, new OpenApiSecurityScheme
|
|
{
|
|
Type = SecuritySchemeType.OAuth2,
|
|
Flows = new OpenApiOAuthFlows
|
|
{
|
|
Implicit = new OpenApiOAuthFlow
|
|
{
|
|
AuthorizationUrl = new Uri("https://login.microsoftonline.com/common/oauth2/v2.0/authorize"),
|
|
TokenUrl = new Uri("https://login.microsoftonline.com/common/oauth2/v2.0/token"),
|
|
Scopes = new Dictionary<string, string>
|
|
{
|
|
{ "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/DefaultScope", "Default Scope" }
|
|
}
|
|
}
|
|
},
|
|
Scheme = "Bearer",
|
|
BearerFormat = "JWT",
|
|
In = ParameterLocation.Header,
|
|
});
|
|
// This adds the lock symbol next to every route in SwaggerUI.
|
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|
{
|
|
{
|
|
new OpenApiSecurityScheme{ Reference = new OpenApiReference{ Type = ReferenceType.SecurityScheme, Id = bearerKey } },
|
|
Array.Empty<string>()
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|