using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using PlaylistShared.Api.Data; using PlaylistShared.Api.Entities; using PlaylistShared.Api.Mapping; using PlaylistShared.Api.Services; using System.IdentityModel.Tokens.Jwt; using System.Text; namespace PlaylistShared.Api; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); // DbContext builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Identity builder.Services.AddIdentity>(options => { options.User.RequireUniqueEmail = true; options.Password.RequireDigit = false; options.Password.RequiredLength = 6; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; }) .AddEntityFrameworkStores() .AddDefaultTokenProviders(); // Session builder.Services.AddDistributedSqlServerCache(options => { options.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"); options.SchemaName = "dbo"; options.TableName = "SessionCache"; }); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromDays(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.Cookie.SameSite = SameSiteMode.Lax; }); // JWT var jwtKey = builder.Configuration["Jwt:Key"] ?? throw new Exception("Jwt:Key missing"); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)) }; }) .AddOpenIdConnect("Keycloak", options => { options.Authority = builder.Configuration["Keycloak:Authority"]; options.ClientId = builder.Configuration["Keycloak:ClientId"]; options.ClientSecret = builder.Configuration["Keycloak:ClientSecret"]; options.ResponseType = "code"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); options.CallbackPath = "/api/auth/keycloak-callback"; options.SignInScheme = IdentityConstants.ExternalScheme; }); builder.Services.AddHttpContextAccessor(); builder.Services.AddAuthorization(); builder.Services.AddAutoMapper(t => t.AddProfile()); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddDataProtection(); builder.Services.AddHttpClient(); builder.Services.AddCors(options => { options.AddPolicy("Production", policy => { policy.WithOrigins(builder.Configuration.GetSection("Cors:Origins").Get()) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); app.UseSwagger(); app.UseSwaggerUI(); app.UseCors("Production"); if (!app.Environment.IsDevelopment()) { app.UseHttpsRedirection(); } app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); } }