Files
Shogi/Shogi.Api/Program.cs
Lucas Morgan 26fd955aa4 Fix claims.
Use OID instead of email for microsoft identifier.
Fix PlayerCount route.
Add created date to user table.
Create spectator icon.
2023-01-20 20:48:38 -06:00

237 lines
9.6 KiB
C#

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Identity.Web;
using Microsoft.OpenApi.Models;
using Shogi.Api.Managers;
using Shogi.Api.Repositories;
using Shogi.Api.Services;
using System.Text;
namespace Shogi.Api
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var allowedOrigins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? throw new InvalidOperationException("Configuration for allowed origins is missing.");
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy
.WithOrigins(allowedOrigins)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.WithExposedHeaders("Set-Cookie")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
ConfigureAuthentication(builder);
ConfigureControllers(builder);
ConfigureSwagger(builder);
ConfigureDependencyInjection(builder);
ConfigureLogging(builder);
var app = builder.Build();
app.UseWhen(
// Log anything that isn't related to swagger.
context => ShouldLog(context),
appBuilder => appBuilder.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.
}
UseCorsAndWebSockets(app, allowedOrigins);
app.UseAuthentication();
app.UseAuthorization();
app.Map("/", () => "OK");
app.MapControllers();
app.Run();
static bool ShouldLog(HttpContext context)
{
var path = context.Request.GetEncodedPathAndQuery();
return !path.Contains("swagger")
&& !path.Equals("/", StringComparison.Ordinal);
}
}
private static void UseCorsAndWebSockets(WebApplication app, string[] allowedOrigins)
{
// TODO: Figure out how to make a middleware for sockets?
var socketService = app.Services.GetRequiredService<ISocketService>();
var socketOptions = new WebSocketOptions();
foreach (var origin in allowedOrigins)
socketOptions.AllowedOrigins.Add(origin);
app.UseCors();
app.UseWebSockets(socketOptions);
app.Use(async (context, next) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
await socketService.HandleSocketRequest(context);
}
await next();
});
}
private static void ConfigureLogging(WebApplicationBuilder builder)
{
builder.Services.AddHttpLogging(options =>
{
options.LoggingFields = HttpLoggingFields.RequestProperties
| HttpLoggingFields.RequestBody
| HttpLoggingFields.ResponseStatusCode
| HttpLoggingFields.ResponseBody;
});
}
private static void ConfigureAuthentication(WebApplicationBuilder builder)
{
AddJwtAuth(builder);
AddCookieAuth(builder);
SetupAuthSwitch(builder);
static void AddJwtAuth(WebApplicationBuilder builder)
{
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
}
static void AddCookieAuth(WebApplicationBuilder builder)
{
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "session-id";
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.SlidingExpiration = true;
options.LoginPath = new PathString("/User/LoginAsGuest");
});
}
static void SetupAuthSwitch(WebApplicationBuilder builder)
{
var defaultScheme = "CookieOrJwt";
builder.Services
.AddAuthentication(defaultScheme)
.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;
};
});
builder
.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = defaultScheme;
});
}
}
private static void ConfigureControllers(WebApplicationBuilder builder)
{
builder.Services.AddControllers();
builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.WriteIndented = true;
});
}
private static void ConfigureDependencyInjection(WebApplicationBuilder builder)
{
var services = builder.Services;
services.AddSingleton<ISocketConnectionManager, SocketConnectionManager>();
services.AddSingleton<ISocketTokenCache, SocketTokenCache>();
services.AddSingleton<ISocketService, SocketService>();
services.AddTransient<IClaimsTransformation, ShogiUserClaimsTransformer>();
services.AddTransient<IShogiUserClaimsTransformer, ShogiUserClaimsTransformer>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISessionRepository, SessionRepository>();
services.AddTransient<IQueryRespository, QueryRepository>();
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" },
{ "profile", "profile" },
{ "openid", "openid" }
}
}
},
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>()
}
});
});
}
}
}