168 lines
5.7 KiB
C#
168 lines
5.7 KiB
C#
using MemberCenter.Infrastructure.Configuration;
|
|
using MemberCenter.Infrastructure.Identity;
|
|
using MemberCenter.Infrastructure.Persistence;
|
|
using MemberCenter.Infrastructure.Services;
|
|
using MemberCenter.Application.Abstractions;
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using OpenIddict.Abstractions;
|
|
using OpenIddict.Server.AspNetCore;
|
|
using OpenIddict.Validation.AspNetCore;
|
|
|
|
EnvLoader.LoadDotEnvIfDevelopment();
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
var pathBase = NormalizePathBase(builder.Configuration["PathBase"]);
|
|
|
|
builder.Services.AddDbContext<MemberCenterDbContext>(options =>
|
|
{
|
|
var connectionString = builder.Configuration.GetConnectionString("Default")
|
|
?? "Host=localhost;Database=member_center;Username=postgres;Password=postgres";
|
|
|
|
options.UseNpgsql(connectionString);
|
|
options.UseOpenIddict();
|
|
});
|
|
|
|
builder.Services
|
|
.AddIdentity<ApplicationUser, ApplicationRole>(options =>
|
|
{
|
|
options.User.RequireUniqueEmail = true;
|
|
options.Password.RequireDigit = true;
|
|
options.Password.RequireLowercase = true;
|
|
options.Password.RequireUppercase = true;
|
|
options.Password.RequireNonAlphanumeric = false;
|
|
options.Password.RequiredLength = 8;
|
|
})
|
|
.AddEntityFrameworkStores<MemberCenterDbContext>()
|
|
.AddDefaultTokenProviders();
|
|
|
|
builder.Services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
|
options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
|
options.DefaultChallengeScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
|
});
|
|
|
|
builder.Services.AddOpenIddict()
|
|
.AddCore(options =>
|
|
{
|
|
options.UseEntityFrameworkCore()
|
|
.UseDbContext<MemberCenterDbContext>();
|
|
})
|
|
.AddServer(options =>
|
|
{
|
|
options.SetAuthorizationEndpointUris(WithPathBase(pathBase, "/oauth/authorize"));
|
|
options.SetTokenEndpointUris(
|
|
WithPathBase(pathBase, "/oauth/token"),
|
|
WithPathBase(pathBase, "/auth/login"),
|
|
WithPathBase(pathBase, "/auth/refresh"));
|
|
options.SetLogoutEndpointUris(WithPathBase(pathBase, "/auth/logout"));
|
|
var issuer = builder.Configuration["Auth:Issuer"];
|
|
if (!string.IsNullOrWhiteSpace(issuer))
|
|
{
|
|
if (!Uri.TryCreate(issuer, UriKind.Absolute, out var issuerUri))
|
|
{
|
|
throw new InvalidOperationException("Auth:Issuer must be an absolute URI.");
|
|
}
|
|
|
|
options.SetIssuer(issuerUri);
|
|
}
|
|
|
|
options.AllowAuthorizationCodeFlow()
|
|
.RequireProofKeyForCodeExchange();
|
|
options.AllowRefreshTokenFlow();
|
|
options.AllowPasswordFlow();
|
|
options.AllowClientCredentialsFlow();
|
|
|
|
options.AcceptAnonymousClients();
|
|
|
|
options.RegisterScopes(
|
|
OpenIddictConstants.Scopes.OpenId,
|
|
OpenIddictConstants.Scopes.Email,
|
|
OpenIddictConstants.Scopes.Profile,
|
|
"newsletter:list.read",
|
|
"newsletter:send.write",
|
|
"newsletter:send.read",
|
|
"newsletter:events.read",
|
|
"newsletter:events.write",
|
|
"newsletter:events.write.global");
|
|
|
|
options.AddDevelopmentEncryptionCertificate();
|
|
options.AddDevelopmentSigningCertificate();
|
|
options.DisableAccessTokenEncryption();
|
|
|
|
var aspNetCore = options.UseAspNetCore()
|
|
.EnableAuthorizationEndpointPassthrough()
|
|
.EnableTokenEndpointPassthrough()
|
|
.EnableLogoutEndpointPassthrough()
|
|
.EnableStatusCodePagesIntegration();
|
|
|
|
if (builder.Environment.IsDevelopment())
|
|
{
|
|
// TEST/LOCAL ONLY: allow HTTP for local Docker integration testing.
|
|
aspNetCore.DisableTransportSecurityRequirement();
|
|
}
|
|
})
|
|
.AddValidation(options =>
|
|
{
|
|
options.UseLocalServer();
|
|
options.UseAspNetCore();
|
|
});
|
|
|
|
builder.Services.AddAuthorization(options =>
|
|
{
|
|
options.AddPolicy("Admin", policy => policy.RequireRole("admin"));
|
|
});
|
|
|
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|
{
|
|
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
options.KnownNetworks.Clear();
|
|
options.KnownProxies.Clear();
|
|
});
|
|
|
|
builder.Services.AddControllers();
|
|
builder.Services.AddScoped<INewsletterService, NewsletterService>();
|
|
builder.Services.AddScoped<IEmailBlacklistService, EmailBlacklistService>();
|
|
builder.Services.AddScoped<ITenantService, TenantService>();
|
|
builder.Services.AddScoped<INewsletterListService, NewsletterListService>();
|
|
builder.Services.Configure<SendEngineWebhookOptions>(builder.Configuration.GetSection("SendEngine"));
|
|
builder.Services.AddHttpClient<SendEngineWebhookPublisher>();
|
|
builder.Services.AddScoped<ISendEngineWebhookPublisher, SendEngineWebhookPublisher>();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseForwardedHeaders();
|
|
if (!string.IsNullOrWhiteSpace(pathBase))
|
|
{
|
|
app.UsePathBase(pathBase);
|
|
}
|
|
|
|
app.UseRouting();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapControllers();
|
|
|
|
app.Run();
|
|
|
|
static string? NormalizePathBase(string? pathBase)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(pathBase))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var normalized = pathBase.StartsWith('/') ? pathBase : $"/{pathBase}";
|
|
return normalized.Length > 1 ? normalized.TrimEnd('/') : normalized;
|
|
}
|
|
|
|
static string WithPathBase(string? pathBase, string relativePath)
|
|
{
|
|
var normalizedRelativePath = relativePath.StartsWith('/') ? relativePath : $"/{relativePath}";
|
|
return string.IsNullOrWhiteSpace(pathBase)
|
|
? normalizedRelativePath
|
|
: $"{pathBase}{normalizedRelativePath}";
|
|
}
|