diff --git a/LiteCharms.Features/Api/Configuration/AuthentikSettings.cs b/LiteCharms.Features/Api/Configuration/AuthentikSettings.cs new file mode 100644 index 0000000..3e2fc3a --- /dev/null +++ b/LiteCharms.Features/Api/Configuration/AuthentikSettings.cs @@ -0,0 +1,18 @@ +namespace LiteCharms.Features.Api.Configuration; + +public sealed class AuthentikSettings +{ + public string? Authority { get; set; } + + public string? ApiResourceName { get; set; } + + public string? ApiResourceSecret { get; set; } + + public string? RequiredClaimName { get; set; } + + public string? RequiredClaimNameValue { get; set; } + + public bool RequireHttpsMetadata { get; set; } + + public bool BypassSslErrors { get; set; } +} diff --git a/LiteCharms.Features/Extensions/Api.cs b/LiteCharms.Features/Extensions/Api.cs index ef50663..8bb3b69 100644 --- a/LiteCharms.Features/Extensions/Api.cs +++ b/LiteCharms.Features/Extensions/Api.cs @@ -1,5 +1,6 @@ using LiteCharms.Features.Abstractions; using LiteCharms.Features.Api; +using LiteCharms.Features.Api.Configuration; namespace LiteCharms.Features.Extensions; @@ -8,6 +9,86 @@ public static class Api public const string Books = nameof(Books); public const string Payments = nameof(Payments); + public static IServiceCollection AddAuthentic(this IServiceCollection services, IConfiguration configuration) + { + var authOptions = new AuthentikSettings(); + + configuration.GetSection("Authentik").Bind(authOptions); + + services.Configure(configuration.GetSection(nameof(AuthentikSettings))); + + services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme) + .AddOAuth2Introspection(options => + { + options.Authority = options.Authority; + options.ClientId = options.ClientId; + options.ClientSecret = options.ClientSecret; + options.DiscoveryPolicy.RequireHttps = authOptions.RequireHttpsMetadata; + options.EnableCaching = true; + options.CacheDuration = TimeSpan.FromMinutes(10); + }); + + if (!string.IsNullOrWhiteSpace(authOptions.RequiredClaimName) && !string.IsNullOrWhiteSpace(authOptions.RequiredClaimNameValue)) + services.AddAuthorizationBuilder().AddPolicy("ApiScope", policy => + policy.RequireClaim(authOptions.RequiredClaimName, authOptions.RequiredClaimNameValue)); + else + services.AddAuthorization(); + + return services; + } + + public static IServiceCollection AddApiServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddHttpClient(); + + services.AddApiVersioning(options => + { + options.DefaultApiVersion = new ApiVersion(1); + options.ReportApiVersions = true; + options.AssumeDefaultVersionWhenUnspecified = true; + options.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), + new QueryStringApiVersionReader("version"), + new QueryStringApiVersionReader("version"), + new MediaTypeApiVersionReader("version")); + }) + .AddApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + var urls = configuration["ASPNETCORE_URLS"] ?? configuration["Urls"]; + var healthUrl = "http://localhost:8080/health"; + + if (!string.IsNullOrWhiteSpace(urls)) + { + string firstUrl = urls.Split(';').FirstOrDefault(s => s.Contains("http://"))! + .Replace("0.0.0.0", "localhost") + .Replace("*", "localhost") + .Replace("+", "localhost"); + + healthUrl = $"{firstUrl.TrimEnd('/')}/health"; + } + + services.AddHealthChecksUI(setup => + { + setup.SetNotifyUnHealthyOneTimeUntilChange(); + setup.AddHealthCheckEndpoint("primary, heal", healthUrl); + setup.SetHeaderText("Midrand Books"); + }) + .AddInMemoryStorage(); + + services.AddOutputCache(options => + { + options.AddBasePolicy(builder => builder.Cache()); + options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(10); + }); + + services.AddOpenApi(options => options.AddDocumentTransformer()); + + return services; + } + public static IApplicationBuilder MapEndpoints(this WebApplication app, IDictionary versionGroups) { var endpoints = app.Services.GetRequiredService>(); @@ -44,55 +125,4 @@ public static class Api public static string ToEndpointName(this Type target, string? annotation = "") => $"{target.Name.Replace("Endpoint", string.Empty)}{annotation}".ToLower(CultureInfo.CurrentCulture); - public static IServiceCollection AddApiServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddHttpClient(); - - services.AddApiVersioning(options => - { - options.DefaultApiVersion = new ApiVersion(1); - options.ReportApiVersions = true; - options.AssumeDefaultVersionWhenUnspecified = true; - options.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), - new QueryStringApiVersionReader("version"), - new QueryStringApiVersionReader("version"), - new MediaTypeApiVersionReader("version")); - }) - .AddApiExplorer(options => - { - options.GroupNameFormat = "'v'VVV"; - options.SubstituteApiVersionInUrl = true; - }); - - var urls = configuration["ASPNETCORE_URLS"] ?? configuration["Urls"]; - var healthUrl = "http://localhost:8080/health"; - - if (!string.IsNullOrWhiteSpace(urls)) - { - string firstUrl = urls.Split(';').FirstOrDefault(s => s.Contains("http://"))! - .Replace("0.0.0.0", "localhost") - .Replace("*", "localhost") - .Replace("+", "localhost"); - - healthUrl = $"{firstUrl.TrimEnd('/')}/health"; - } - - services.AddHealthChecksUI(setup => - { - setup.SetNotifyUnHealthyOneTimeUntilChange(); - setup.AddHealthCheckEndpoint("primary, heal", healthUrl); - setup.SetHeaderText("Midrand Books"); - }) - .AddInMemoryStorage(); - - services.AddOutputCache(options => - { - options.AddBasePolicy(builder => builder.Cache()); - options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(10); - }); - - services.AddOpenApi(options => options.AddDocumentTransformer()); - - return services; - } } diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index dd9ff14..4379e7c 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -29,6 +29,18 @@ + + + + + + + + + + + +