using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Abstractions; using Microsoft.Identity.Web; using Microsoft.Identity.Web.Resource; using Microsoft.IdentityModel.Tokens; using System.Text; using Api.Services; namespace Api { using Microsoft.EntityFrameworkCore; using Api.Models; using Microsoft.AspNetCore.Rewrite; public static class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Configure JWT authentication for custom tokens var jwtSettings = builder.Configuration.GetSection("Jwt"); var secretKey = jwtSettings["SecretKey"]; if (string.IsNullOrEmpty(secretKey)) { throw new InvalidOperationException("JWT SecretKey must be configured"); } builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)), ValidateIssuer = true, ValidIssuer = jwtSettings["Issuer"], ValidateAudience = true, ValidAudience = jwtSettings["Audience"], ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5) }; options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { var logger = context.HttpContext.RequestServices.GetRequiredService() .CreateLogger("JwtAuthentication"); logger.LogWarning("JWT authentication failed: {Exception}", context.Exception.Message); return Task.CompletedTask; }, OnTokenValidated = context => { var logger = context.HttpContext.RequestServices.GetRequiredService() .CreateLogger("JwtAuthentication"); var userId = context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; logger.LogInformation("JWT token validated for user {UserId}", userId); return Task.CompletedTask; } }; }); // Add authorization builder.Services.AddAuthorization(); // Register custom services builder.Services.AddHttpClient(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddCors(options => { options.AddPolicy("Default", policy => { var allowedHostsConfiguration = builder.Configuration["CorsOrigins"]? .ToString() .Split(','); policy .WithOrigins(allowedHostsConfiguration ?? new[] { "*" }) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // Allow credentials for JWT tokens }); }); builder.Services.AddControllers(); // Add DbContext with SQL Server builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Name = "Authorization", Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http, Scheme = "Bearer", BearerFormat = "JWT", In = Microsoft.OpenApi.Models.ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme." }); options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement { { new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Reference = new Microsoft.OpenApi.Models.OpenApiReference { Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty() } }); }); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); if (!app.Environment.IsDevelopment()) { app.UseHttpsRedirection(); } // Angular rewrite for SPA hosting var routes = new[] { "api", "swagger" }; var rewriteString = String.Join("|", routes); var rewriteOptions = new RewriteOptions() .AddRewrite(@$"^(?!.*?\b({rewriteString}))^(?!.*?\.\b(jpg|jpeg|png|svg|ttf|woff|woff2|html|js|json|css|ico))", "index.html", false); app.UseRewriter(rewriteOptions); // Serve static files from the Angular app if (app.Environment.IsDevelopment() && Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "../Web/dist/Web/browser"))) { var currentDirectory = Directory.GetCurrentDirectory(); var staticFilePath = Path.Combine(currentDirectory, "../Web/dist/Web/browser"); app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(staticFilePath), DefaultFileNames = new List { "index.html" } }); app.UseStaticFiles(new StaticFileOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(staticFilePath), RequestPath = "" }); } else { app.UseDefaultFiles(); // Uses wwwroot by default app.UseStaticFiles(); } app.UseCors("Default"); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }