diff --git a/CleanArchitecture.Api/CleanArchitecture.Api.csproj b/CleanArchitecture.Api/CleanArchitecture.Api.csproj index 3ffe10b..2ff7087 100644 --- a/CleanArchitecture.Api/CleanArchitecture.Api.csproj +++ b/CleanArchitecture.Api/CleanArchitecture.Api.csproj @@ -7,22 +7,22 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - - - - + + + + diff --git a/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs b/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs new file mode 100644 index 0000000..a71b3ed --- /dev/null +++ b/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.Text; +using CleanArchitecture.Domain.Settings; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; + +namespace CleanArchitecture.Api.Extensions; + +public static class ServiceCollectionExtension +{ + public static IServiceCollection AddSwagger(this IServiceCollection services) + { + services.AddSwaggerGen(c => + { + c.EnableAnnotations(); + + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "CleanArchitecture", + Version = "v1", + Description = "A clean architecture API" + }); + + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. " + + "Use the /api/v1/user/login endpoint to generate a token", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + Scheme = "bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header + }, + new List() + } + }); + }); + return services; + } + + public static IServiceCollection AddAuth(this IServiceCollection services, IConfiguration configuration) + { + services.AddHttpContextAccessor(); + + services.AddAuthentication( + options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) + .AddJwtBearer( + jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(configuration); }); + + services + .AddOptions() + .Bind(configuration.GetSection("Auth")) + .ValidateOnStart(); + + return services; + } + + public static TokenValidationParameters CreateTokenValidationParameters(IConfiguration configuration) + { + var result = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = configuration["Auth:Issuer"], + ValidAudience = configuration["Auth:Audience"], + IssuerSigningKey = new SymmetricSecurityKey( + Encoding.UTF8.GetBytes( + configuration["Auth:Secret"]!)), + RequireSignedTokens = false + }; + + return result; + } +} diff --git a/CleanArchitecture.Api/Program.cs b/CleanArchitecture.Api/Program.cs index b828705..3678e19 100644 --- a/CleanArchitecture.Api/Program.cs +++ b/CleanArchitecture.Api/Program.cs @@ -1,67 +1,22 @@ -using System.Collections.Generic; -using System.Text; +using CleanArchitecture.Api.Extensions; using CleanArchitecture.Application.Extensions; using CleanArchitecture.Domain.Extensions; -using CleanArchitecture.Domain.Settings; using CleanArchitecture.gRPC; using CleanArchitecture.Infrastructure.Database; using CleanArchitecture.Infrastructure.Extensions; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddGrpc(); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(c => -{ - c.EnableAnnotations(); - - c.SwaggerDoc("v1", new OpenApiInfo - { - Title = "CleanArchitecture", - Version = "v1", - Description = "A clean architecture API" - }); - - c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - Description = "JWT Authorization header using the Bearer scheme. " + - "Use the /api/v1/user/login endpoint to generate a token", - Name = "Authorization", - In = ParameterLocation.Header, - Type = SecuritySchemeType.Http, - Scheme = "bearer" - }); - - c.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - }, - Scheme = "oauth2", - Name = "Bearer", - In = ParameterLocation.Header - }, - new List() - } - }); -}); builder.Services.AddHealthChecks(); -builder.Services.AddHttpContextAccessor(); builder.Services.AddDbContext(options => { @@ -70,11 +25,8 @@ builder.Services.AddDbContext(options => b => b.MigrationsAssembly("CleanArchitecture.Infrastructure")); }); -builder.Services.AddAuthentication( - options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) - .AddJwtBearer( - jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(); }); - +builder.Services.AddSwagger(); +builder.Services.AddAuth(builder.Configuration); builder.Services.AddInfrastructure(); builder.Services.AddQueryHandlers(); builder.Services.AddServices(); @@ -82,11 +34,6 @@ builder.Services.AddCommandHandlers(); builder.Services.AddNotificationHandlers(); builder.Services.AddApiUser(); -builder.Services - .AddOptions() - .Bind(builder.Configuration.GetSection("Auth")) - .ValidateOnStart(); - builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); }); var app = builder.Build(); @@ -116,25 +63,6 @@ using (var scope = app.Services.CreateScope()) app.Run(); -TokenValidationParameters CreateTokenValidationParameters() -{ - var result = new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidIssuer = builder.Configuration["Auth:Issuer"], - ValidAudience = builder.Configuration["Auth:Audience"], - IssuerSigningKey = new SymmetricSecurityKey( - Encoding.UTF8.GetBytes( - builder.Configuration["Auth:Secret"]!)), - RequireSignedTokens = false - }; - - return result; -} - // Needed for integration tests web application factory public partial class Program {