From fc554bf7569bbdc6433d033a89492f993e4ed6e8 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 15:55:16 -0700 Subject: [PATCH 01/10] Add OIDC basics --- .../AuthenticationConfigurationService.cs | 31 +++++++++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 20 ++++++++++++ .../Options/AuthenticationOptions.cs | 11 +++++++ .../Options/AuthenticationProviderOptions.cs | 12 +++++++ src/TesApi.Web/Startup.cs | 4 +++ src/TesApi.Web/TesApi.Web.csproj | 1 + 6 files changed, 79 insertions(+) create mode 100644 src/TesApi.Web/AuthenticationConfigurationService.cs create mode 100644 src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs create mode 100644 src/TesApi.Web/Options/AuthenticationOptions.cs create mode 100644 src/TesApi.Web/Options/AuthenticationProviderOptions.cs diff --git a/src/TesApi.Web/AuthenticationConfigurationService.cs b/src/TesApi.Web/AuthenticationConfigurationService.cs new file mode 100644 index 000000000..df888501c --- /dev/null +++ b/src/TesApi.Web/AuthenticationConfigurationService.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace TesApi.Web +{ + public class AuthenticationConfigurationService + { + private readonly TesApi.Web.Options.AuthenticationOptions _authenticationOptions; + + public AuthenticationConfigurationService(IOptions authenticationOptions) + { + _authenticationOptions = authenticationOptions.Value; + } + + public void ConfigureJwtBearer(AuthenticationBuilder builder) + { + foreach (var provider in _authenticationOptions.Providers) + { + builder.AddJwtBearer(provider.Name, options => + { + options.Authority = provider.Authority; + options.Audience = provider.Audience; + }); + } + } + } +} diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..1b6417cd2 --- /dev/null +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Extensions.DependencyInjection; + +namespace TesApi.Web.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services) + { + var serviceProvider = services.BuildServiceProvider(); + var authenticationConfigurationService = serviceProvider.GetRequiredService(); + var authenticationBuilder = services.AddAuthentication(); + authenticationConfigurationService.ConfigureJwtBearer(authenticationBuilder); + + return services; + } + } +} diff --git a/src/TesApi.Web/Options/AuthenticationOptions.cs b/src/TesApi.Web/Options/AuthenticationOptions.cs new file mode 100644 index 000000000..dde6e4ef2 --- /dev/null +++ b/src/TesApi.Web/Options/AuthenticationOptions.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace TesApi.Web.Options +{ + public class AuthenticationOptions + { + public const string SectionName = "Authentication"; + public AuthenticationProviderOptions[] Providers { get; set; } + } +} diff --git a/src/TesApi.Web/Options/AuthenticationProviderOptions.cs b/src/TesApi.Web/Options/AuthenticationProviderOptions.cs new file mode 100644 index 000000000..20fe2707f --- /dev/null +++ b/src/TesApi.Web/Options/AuthenticationProviderOptions.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace TesApi.Web.Options +{ + public class AuthenticationProviderOptions + { + public string Name { get; set; } + public string Authority { get; set; } + public string Audience { get; set; } + } +} diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index e28d2c4d2..64e996066 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -21,6 +21,7 @@ using Tes.Models; using Tes.Repository; using TesApi.Filters; +using TesApi.Web.Extensions; using TesApi.Web.Management; using TesApi.Web.Management.Batch; using TesApi.Web.Management.Configuration; @@ -75,8 +76,11 @@ public void ConfigureServices(IServiceCollection services) .Configure(configuration.GetSection(BatchSchedulingOptions.SectionName)) .Configure(configuration.GetSection(StorageOptions.SectionName)) .Configure(configuration.GetSection(MarthaOptions.SectionName)) + .Configure(configuration.GetSection(AuthenticationOptions.SectionName)) .AddMemoryCache(o => o.ExpirationScanFrequency = TimeSpan.FromHours(12)) + .AddSingleton() + .AddOpenIdConnectAuthentication() .AddSingleton, TesRepositoryCache>() .AddSingleton() .AddSingleton() diff --git a/src/TesApi.Web/TesApi.Web.csproj b/src/TesApi.Web/TesApi.Web.csproj index ea889c7b5..e95680b6b 100644 --- a/src/TesApi.Web/TesApi.Web.csproj +++ b/src/TesApi.Web/TesApi.Web.csproj @@ -17,6 +17,7 @@ + From 4722af49c1b218e341495fb738316a911710ac11 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 16:08:19 -0700 Subject: [PATCH 02/10] fix DI --- .../Extensions/ServiceCollectionExtensions.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index 1b6417cd2..f1001e253 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Linq; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using TesApi.Web.Options; namespace TesApi.Web.Extensions { @@ -10,9 +13,14 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services) { var serviceProvider = services.BuildServiceProvider(); - var authenticationConfigurationService = serviceProvider.GetRequiredService(); - var authenticationBuilder = services.AddAuthentication(); - authenticationConfigurationService.ConfigureJwtBearer(authenticationBuilder); + var authenticationOptions = serviceProvider.GetService>(); + + if (authenticationOptions?.Value?.Providers?.Any() == true) + { + var authenticationConfigurationService = serviceProvider.GetRequiredService(); + var authenticationBuilder = services.AddAuthentication(); + authenticationConfigurationService.ConfigureJwtBearer(authenticationBuilder); + } return services; } From 14b2a5a4b08cd2963e58848126a3712ad1d67278 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 16:39:58 -0700 Subject: [PATCH 03/10] add auth to mvc --- .../Extensions/ServiceCollectionExtensions.cs | 4 +++- src/TesApi.Web/Startup.cs | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index f1001e253..766386127 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -10,8 +10,9 @@ namespace TesApi.Web.Extensions { public static class ServiceCollectionExtensions { - public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services) + public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services, out bool isAuthConfigured) { + isAuthConfigured = false; var serviceProvider = services.BuildServiceProvider(); var authenticationOptions = serviceProvider.GetService>(); @@ -20,6 +21,7 @@ public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCol var authenticationConfigurationService = serviceProvider.GetRequiredService(); var authenticationBuilder = services.AddAuthentication(); authenticationConfigurationService.ConfigureJwtBearer(authenticationBuilder); + isAuthConfigured = true; } return services; diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index 64e996066..ca47ac501 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -8,6 +8,8 @@ using Azure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -80,7 +82,7 @@ public void ConfigureServices(IServiceCollection services) .AddMemoryCache(o => o.ExpirationScanFrequency = TimeSpan.FromHours(12)) .AddSingleton() - .AddOpenIdConnectAuthentication() + .AddOpenIdConnectAuthentication(out bool isAuthConfigured) .AddSingleton, TesRepositoryCache>() .AddSingleton() .AddSingleton() @@ -88,7 +90,18 @@ public void ConfigureServices(IServiceCollection services) .AddSingleton() .AddSingleton(CreateBatchPoolManagerFromConfiguration) - .AddControllers(options => options.Filters.Add()) + .AddControllers(options => + { + options.Filters.Add(); + + if (isAuthConfigured) + { + services.Configure(options => + { + options.Filters.Add(new AuthorizeFilter()); + }); + } + }) .AddNewtonsoftJson(opts => { opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); From c003b3fa524676370bc55ca9002e69ccc0ab5478 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 17:07:45 -0700 Subject: [PATCH 04/10] refactor --- .../AuthenticationConfigurationService.cs | 31 -------------- .../Extensions/ServiceCollectionExtensions.cs | 42 ++++++++++++++----- src/TesApi.Web/Startup.cs | 30 ++----------- 3 files changed, 36 insertions(+), 67 deletions(-) delete mode 100644 src/TesApi.Web/AuthenticationConfigurationService.cs diff --git a/src/TesApi.Web/AuthenticationConfigurationService.cs b/src/TesApi.Web/AuthenticationConfigurationService.cs deleted file mode 100644 index df888501c..000000000 --- a/src/TesApi.Web/AuthenticationConfigurationService.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace TesApi.Web -{ - public class AuthenticationConfigurationService - { - private readonly TesApi.Web.Options.AuthenticationOptions _authenticationOptions; - - public AuthenticationConfigurationService(IOptions authenticationOptions) - { - _authenticationOptions = authenticationOptions.Value; - } - - public void ConfigureJwtBearer(AuthenticationBuilder builder) - { - foreach (var provider in _authenticationOptions.Providers) - { - builder.AddJwtBearer(provider.Name, options => - { - options.Authority = provider.Authority; - options.Audience = provider.Audience; - }); - } - } - } -} diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index 766386127..481bc0dc7 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -2,28 +2,50 @@ // Licensed under the MIT License. using System.Linq; +using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using TesApi.Web.Options; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; namespace TesApi.Web.Extensions { public static class ServiceCollectionExtensions { - public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services, out bool isAuthConfigured) + public static IServiceCollection ConfigureAuthenticationAndControllers(this IServiceCollection services, Options.AuthenticationOptions authenticationOptions) { - isAuthConfigured = false; - var serviceProvider = services.BuildServiceProvider(); - var authenticationOptions = serviceProvider.GetService>(); + bool isAuthConfigured = false; - if (authenticationOptions?.Value?.Providers?.Any() == true) + if (authenticationOptions?.Providers?.Any() == true) { - var authenticationConfigurationService = serviceProvider.GetRequiredService(); - var authenticationBuilder = services.AddAuthentication(); - authenticationConfigurationService.ConfigureJwtBearer(authenticationBuilder); + var authBuilder = services.AddAuthentication(); + + foreach (var provider in authenticationOptions.Providers) + { + authBuilder.AddJwtBearer(provider.Name, options => + { + options.Authority = provider.Authority; + options.Audience = provider.Audience; + }); + } + isAuthConfigured = true; } + services.AddControllers(options => + { + options.Filters.Add(); + + if (isAuthConfigured) + { + options.Filters.Add(new AuthorizeFilter()); + } + }) + .AddNewtonsoftJson(opts => + { + opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + opts.SerializerSettings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy())); + }); + return services; } } diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index ca47ac501..159d8d773 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -8,16 +8,11 @@ using Azure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; using Tes.ApiClients; using Tes.ApiClients.Options; using Tes.Models; @@ -62,6 +57,9 @@ public void ConfigureServices(IServiceCollection services) { try { + var authenticationOptions = new AuthenticationOptions(); + configuration.GetSection(AuthenticationOptions.SectionName)?.Bind(authenticationOptions); + services .AddLogging() .AddApplicationInsightsTelemetry(configuration) @@ -81,8 +79,7 @@ public void ConfigureServices(IServiceCollection services) .Configure(configuration.GetSection(AuthenticationOptions.SectionName)) .AddMemoryCache(o => o.ExpirationScanFrequency = TimeSpan.FromHours(12)) - .AddSingleton() - .AddOpenIdConnectAuthentication(out bool isAuthConfigured) + .ConfigureAuthenticationAndControllers(authenticationOptions) .AddSingleton, TesRepositoryCache>() .AddSingleton() .AddSingleton() @@ -90,25 +87,6 @@ public void ConfigureServices(IServiceCollection services) .AddSingleton() .AddSingleton(CreateBatchPoolManagerFromConfiguration) - .AddControllers(options => - { - options.Filters.Add(); - - if (isAuthConfigured) - { - services.Configure(options => - { - options.Filters.Add(new AuthorizeFilter()); - }); - } - }) - .AddNewtonsoftJson(opts => - { - opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - opts.SerializerSettings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy())); - }) - .Services - .AddSingleton(CreateStorageAccessProviderFromConfiguration) .AddSingleton(sp => ActivatorUtilities.CreateInstance(sp, (IAzureProxy)sp.GetRequiredService(typeof(AzureProxy)))) .AddSingleton>(sp => ActivatorUtilities.CreateInstance>(sp, (IRepository)sp.GetRequiredService(typeof(TesTaskPostgreSqlRepository)))) From ac7e9eb18e793a7cd033da16852b37d4f4866fd0 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 17:17:11 -0700 Subject: [PATCH 05/10] remove unnecessary auth settings --- src/TesApi.Web/Startup.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index 159d8d773..ffedaeade 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -76,7 +76,6 @@ public void ConfigureServices(IServiceCollection services) .Configure(configuration.GetSection(BatchSchedulingOptions.SectionName)) .Configure(configuration.GetSection(StorageOptions.SectionName)) .Configure(configuration.GetSection(MarthaOptions.SectionName)) - .Configure(configuration.GetSection(AuthenticationOptions.SectionName)) .AddMemoryCache(o => o.ExpirationScanFrequency = TimeSpan.FromHours(12)) .ConfigureAuthenticationAndControllers(authenticationOptions) From 4a247a97b6a7ec3eaeaee93d809e73218d51d412 Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 17:18:12 -0700 Subject: [PATCH 06/10] not required to check existence with Bind --- src/TesApi.Web/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index ffedaeade..12edb2bb9 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -58,7 +58,7 @@ public void ConfigureServices(IServiceCollection services) try { var authenticationOptions = new AuthenticationOptions(); - configuration.GetSection(AuthenticationOptions.SectionName)?.Bind(authenticationOptions); + configuration.GetSection(AuthenticationOptions.SectionName).Bind(authenticationOptions); services .AddLogging() From dee64d10d96f3577d0c5ff44d5155da8fc71a0cf Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 17:22:35 -0700 Subject: [PATCH 07/10] simplify iconfig --- src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs | 7 ++++++- src/TesApi.Web/Startup.cs | 5 +---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index 481bc0dc7..c98fb7593 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -3,18 +3,23 @@ using System.Linq; using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; +using TesApi.Web.Options; namespace TesApi.Web.Extensions { public static class ServiceCollectionExtensions { - public static IServiceCollection ConfigureAuthenticationAndControllers(this IServiceCollection services, Options.AuthenticationOptions authenticationOptions) + public static IServiceCollection ConfigureAuthenticationAndControllers(this IServiceCollection services, IConfiguration configuration) { bool isAuthConfigured = false; + var authenticationOptions = new AuthenticationOptions(); + configuration.GetSection(AuthenticationOptions.SectionName).Bind(authenticationOptions); + if (authenticationOptions?.Providers?.Any() == true) { var authBuilder = services.AddAuthentication(); diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index 12edb2bb9..4084bebd3 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -57,9 +57,6 @@ public void ConfigureServices(IServiceCollection services) { try { - var authenticationOptions = new AuthenticationOptions(); - configuration.GetSection(AuthenticationOptions.SectionName).Bind(authenticationOptions); - services .AddLogging() .AddApplicationInsightsTelemetry(configuration) @@ -76,9 +73,9 @@ public void ConfigureServices(IServiceCollection services) .Configure(configuration.GetSection(BatchSchedulingOptions.SectionName)) .Configure(configuration.GetSection(StorageOptions.SectionName)) .Configure(configuration.GetSection(MarthaOptions.SectionName)) + .ConfigureAuthenticationAndControllers(configuration) .AddMemoryCache(o => o.ExpirationScanFrequency = TimeSpan.FromHours(12)) - .ConfigureAuthenticationAndControllers(authenticationOptions) .AddSingleton, TesRepositoryCache>() .AddSingleton() .AddSingleton() From e4580c105b3d670f953ae0e8f4271ef7b156690a Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 17:25:11 -0700 Subject: [PATCH 08/10] refactor --- src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index c98fb7593..e9424655c 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -15,12 +15,11 @@ public static class ServiceCollectionExtensions { public static IServiceCollection ConfigureAuthenticationAndControllers(this IServiceCollection services, IConfiguration configuration) { - bool isAuthConfigured = false; - var authenticationOptions = new AuthenticationOptions(); configuration.GetSection(AuthenticationOptions.SectionName).Bind(authenticationOptions); + var isAuthConfigured = authenticationOptions?.Providers?.Any() == true; - if (authenticationOptions?.Providers?.Any() == true) + if (isAuthConfigured) { var authBuilder = services.AddAuthentication(); @@ -32,8 +31,6 @@ public static IServiceCollection ConfigureAuthenticationAndControllers(this ISer options.Audience = provider.Audience; }); } - - isAuthConfigured = true; } services.AddControllers(options => From 7730707dfdd07c603a25b14e064f1ce240c9efde Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 20:08:39 -0700 Subject: [PATCH 09/10] add oidc --- .../Extensions/ServiceCollectionExtensions.cs | 38 ++++++++-- .../Options/AuthenticationProviderOptions.cs | 2 +- src/TesApi.Web/Startup.cs | 72 +++++++++++-------- src/TesApi.Web/TesApi.Web.csproj | 1 + .../scripts/helm/templates/service.yaml | 1 + 5 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index e9424655c..840a7c970 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -2,9 +2,13 @@ // Licensed under the MIT License. using System.Linq; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using TesApi.Web.Options; @@ -21,16 +25,38 @@ public static IServiceCollection ConfigureAuthenticationAndControllers(this ISer if (isAuthConfigured) { - var authBuilder = services.AddAuthentication(); + var authBuilder = services.AddAuthentication(options => + { + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }); foreach (var provider in authenticationOptions.Providers) { - authBuilder.AddJwtBearer(provider.Name, options => - { - options.Authority = provider.Authority; - options.Audience = provider.Audience; - }); + authBuilder + .AddJwtBearer(provider.Name, options => + { + options.Authority = provider.Authority; + options.Audience = provider.ClientId; // ClientId and Audience are synonymous + }) + .AddOpenIdConnect(options => + { + options.Authority = provider.Authority; // $"https://login.microsoftonline.com/{provider.TenantId}/v2.0"; + options.MetadataAddress = $"{options.Authority.TrimStart('/')}/.well-known/openid-configuration"; + options.ClientId = provider.ClientId; // Same as audience + options.ResponseType = OpenIdConnectResponseType.Code; + options.SaveTokens = false; // TODO in the future can use this to make authorized calls to Azure Storage + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = provider.Authority, + ValidateAudience = true, + ValidAudience = provider.ClientId + }; + }); } + + services.AddAuthorization(); } services.AddControllers(options => diff --git a/src/TesApi.Web/Options/AuthenticationProviderOptions.cs b/src/TesApi.Web/Options/AuthenticationProviderOptions.cs index 20fe2707f..55c050dc2 100644 --- a/src/TesApi.Web/Options/AuthenticationProviderOptions.cs +++ b/src/TesApi.Web/Options/AuthenticationProviderOptions.cs @@ -7,6 +7,6 @@ public class AuthenticationProviderOptions { public string Name { get; set; } public string Authority { get; set; } - public string Audience { get; set; } + public string ClientId { get; set; } } } diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index 4084bebd3..0adc4a05f 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -3,11 +3,13 @@ using System; using System.IO; +using System.Linq; using System.Reflection; using Azure.Core; using Azure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -255,37 +257,51 @@ BatchAccountResourceInformation CreateBatchAccountResourceInformation(IServicePr /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. /// /// An Microsoft.AspNetCore.Builder.IApplicationBuilder for the app to configure. - public void Configure(IApplicationBuilder app) - => app.UseRouting() - .UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }) - - .UseHttpsRedirection() - - .UseDefaultFiles() - .UseStaticFiles() - .UseSwagger(c => - { - c.RouteTemplate = "swagger/{documentName}/openapi.json"; - }) - .UseSwaggerUI(c => - { - c.SwaggerEndpoint($"/swagger/{tesVersion}/openapi.json", "Task Execution Service"); - }) - - .IfThenElse(hostingEnvironment.IsDevelopment(), + public void Configure(IApplicationBuilder app) => app + .UseForwardedHeaders(new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto + }) + .UseDefaultFiles() + .UseStaticFiles() + .UseSwagger(c => + { + c.RouteTemplate = "swagger/{documentName}/openapi.json"; + }) + .UseSwaggerUI(c => + { + c.SwaggerEndpoint($"/swagger/{tesVersion}/openapi.json", "Task Execution Service"); + }) + .UseRouting() + .UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }) + .IfThenElse(app + .ApplicationServices + .GetRequiredService>() + ?.Value + ?.Schemes + ?.Any() == true, s => { - var r = s.UseDeveloperExceptionPage(); - logger.LogInformation("Configuring for Development environment"); + app.UseAuthentication(); + app.UseAuthorization(); }, - s => - { - var r = s.UseHsts(); - logger.LogInformation("Configuring for Production environment"); - }); + s => { }) + .IfThenElse(hostingEnvironment.IsDevelopment(), + s => + { + s.UseDeveloperExceptionPage(); + logger.LogInformation("Configuring for Development environment"); + }, + s => + { + s.UseHttpsRedirection(); + s.UseHsts(); + logger.LogInformation("Configuring for Production environment"); + }); + } internal static class BooleanMethodSelectorExtensions diff --git a/src/TesApi.Web/TesApi.Web.csproj b/src/TesApi.Web/TesApi.Web.csproj index e95680b6b..8cb76e9e3 100644 --- a/src/TesApi.Web/TesApi.Web.csproj +++ b/src/TesApi.Web/TesApi.Web.csproj @@ -18,6 +18,7 @@ + diff --git a/src/deploy-tes-on-azure/scripts/helm/templates/service.yaml b/src/deploy-tes-on-azure/scripts/helm/templates/service.yaml index 6421fa02d..a6ce249aa 100644 --- a/src/deploy-tes-on-azure/scripts/helm/templates/service.yaml +++ b/src/deploy-tes-on-azure/scripts/helm/templates/service.yaml @@ -43,6 +43,7 @@ metadata: nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: tes-basic-auth nginx.ingress.kubernetes.io/auth-realm: "Authentication required" + nginx.ingress.kubernetes.io/proxy-set-headers: "X-Real-IP $remote_addr, X-Forwarded-For $proxy_add_x_forwarded_for, X-Forwarded-Proto $scheme" spec: ingressClassName: nginx tls: From 6ada0a46d55e1cb885f72d805e472e0bf113332d Mon Sep 17 00:00:00 2001 From: Matt McLoughlin Date: Wed, 4 Oct 2023 21:32:15 -0700 Subject: [PATCH 10/10] add comment --- src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs index 840a7c970..b5dc9c238 100644 --- a/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/TesApi.Web/Extensions/ServiceCollectionExtensions.cs @@ -65,6 +65,8 @@ public static IServiceCollection ConfigureAuthenticationAndControllers(this ISer if (isAuthConfigured) { + // Adds authorization to all controllers and operations + // TODO might want to expose ServiceInfo with the Authority and MetadataAddress and Audience options.Filters.Add(new AuthorizeFilter()); } })