106 lines
4.3 KiB
C#
106 lines
4.3 KiB
C#
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using SIFinance.Api;
|
|
using SIFinance.Modules.Achats.Endpoints;
|
|
using SIFinance.Modules.Finance.Endpoints;
|
|
using SIFinance.Modules.SousTraitants.Endpoints;
|
|
using SIFinance.Shared;
|
|
using SIFinance.Shared.Entities;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddOpenApi();
|
|
|
|
builder.Services.AddDbContext<SIFinanceDbContext>(options =>
|
|
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
|
|
|
|
builder.Services.AddHostedService<DbKeepAliveService>();
|
|
|
|
builder.Services.AddHttpClient();
|
|
|
|
builder.Services.Configure<AzureAdOptions>(builder.Configuration.GetSection(AzureAdOptions.SectionName));
|
|
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection(JwtOptions.SectionName));
|
|
builder.Services.AddSingleton<EntraGraphTokenProvider>();
|
|
builder.Services.AddSingleton<JwtTokenService>();
|
|
builder.Services.AddHostedService<EntraSyncService>();
|
|
|
|
// Valide le jeton "maison" émis par SIFinance.Api après l'échange OAuth2 avec Entra ID
|
|
// (voir EntraAuthEndpoints) — pas de validation directe de jeton Entra ID : le backend
|
|
// pilote lui-même le flux de connexion, comme l'appli NDF existante (JWT maison +
|
|
// synchro périodique du groupe Entra ID via EntraSyncService), plutôt que MSAL.js +
|
|
// Microsoft.Identity.Web côté frontend.
|
|
var jwtSecret = builder.Configuration["Jwt:Secret"]
|
|
?? throw new InvalidOperationException("Configuration Jwt:Secret manquante — voir appsettings.Development.json.");
|
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = false,
|
|
ValidateAudience = false,
|
|
ValidateLifetime = true,
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(jwtSecret)),
|
|
};
|
|
});
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddScoped<CurrentUserService>();
|
|
|
|
builder.Services.Configure<SharePointOptions>(builder.Configuration.GetSection(SharePointOptions.SectionName));
|
|
|
|
// Pas de client Graph enregistré directement en DI : tant que TenantId/ClientId/
|
|
// ClientSecret ne sont pas renseignés (ou invalides), ClientSecretCredential lève une
|
|
// ArgumentException dès sa construction — avant même le premier appel Graph. Si le
|
|
// client était un singleton construit par le conteneur DI, cette exception surviendrait
|
|
// pendant la résolution de la requête, hors de portée d'un try/catch dans l'endpoint,
|
|
// et remonterait comme une erreur 500 brute. SharePointGraphClientFactory construit le
|
|
// client à la demande, à l'intérieur du bloc try/catch de l'endpoint.
|
|
builder.Services.AddSingleton<SharePointGraphClientFactory>();
|
|
builder.Services.AddSingleton<SharePointUploadService>();
|
|
builder.Services.AddSingleton<NotificationEmailService>();
|
|
|
|
const string FrontendCorsPolicy = "Frontend";
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddPolicy(FrontendCorsPolicy, policy =>
|
|
policy.WithOrigins(
|
|
"http://localhost:5173", "http://localhost:5174",
|
|
"http://127.0.0.1:5173", "http://127.0.0.1:5174")
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod()
|
|
// Le navigateur ne mémorise le résultat d'un preflight OPTIONS que sur la durée
|
|
// indiquée ici (par défaut ~5s) : on l'étend pour éviter un aller-retour OPTIONS
|
|
// avant chaque POST/PUT/PATCH répété (création/édition) pendant la session.
|
|
.SetPreflightMaxAge(TimeSpan.FromMinutes(30)));
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.MapOpenApi();
|
|
}
|
|
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseHttpsRedirection();
|
|
}
|
|
app.UseCors(FrontendCorsPolicy);
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapAchatsEndpoints();
|
|
app.MapContratsEndpoints();
|
|
app.MapBudgetsEndpoints();
|
|
app.MapFacturesEndpoints();
|
|
app.MapAvoirsEndpoints();
|
|
app.MapPaiementsEndpoints();
|
|
app.MapDashboardEndpoints();
|
|
app.MapSharePointEndpoints();
|
|
app.MapEntraAuthEndpoints();
|
|
app.MapOrganismesEndpoints();
|
|
app.MapContratsPrestatairesEndpoints();
|
|
|
|
app.Run(); |