diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs
index d1ecd6d0f6..5692311c73 100644
--- a/src/Service.Tests/Configuration/ConfigurationTests.cs
+++ b/src/Service.Tests/Configuration/ConfigurationTests.cs
@@ -9,6 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
using Azure.DataApiBuilder.Config;
+using Azure.DataApiBuilder.Service.AuthenticationHelpers;
using Azure.DataApiBuilder.Service.Authorization;
using Azure.DataApiBuilder.Service.Configurations;
using Azure.DataApiBuilder.Service.Controllers;
@@ -810,6 +811,64 @@ public async Task TestPathRewriteMiddlewareForGraphQL(
}
}
+ ///
+ /// Tests that Startup.cs properly handles EasyAuth authentication configuration.
+ /// AppService as Identity Provider while in Production mode will result in startup error.
+ /// An Azure AppService environment has environment variables on the host which indicate
+ /// the environment is, in fact, an AppService environment.
+ ///
+ /// HostMode in Runtime config - Development or Production.
+ /// EasyAuth auth type - AppService or StaticWebApps.
+ /// Whether to set the AppService host environment variables.
+ /// Whether an error is expected.
+ [DataTestMethod]
+ [TestCategory(TestCategory.MSSQL)]
+ [DataRow(HostModeType.Development, EasyAuthType.AppService, false, false, DisplayName = "AppService Dev - No EnvVars - No Error")]
+ [DataRow(HostModeType.Development, EasyAuthType.AppService, true, false, DisplayName = "AppService Dev - EnvVars - No Error")]
+ [DataRow(HostModeType.Production, EasyAuthType.AppService, false, true, DisplayName = "AppService Prod - No EnvVars - Error")]
+ [DataRow(HostModeType.Production, EasyAuthType.AppService, true, false, DisplayName = "AppService Prod - EnvVars - Error")]
+ [DataRow(HostModeType.Development, EasyAuthType.StaticWebApps, false, false, DisplayName = "SWA Dev - No EnvVars - No Error")]
+ [DataRow(HostModeType.Development, EasyAuthType.StaticWebApps, true, false, DisplayName = "SWA Dev - EnvVars - No Error")]
+ [DataRow(HostModeType.Production, EasyAuthType.StaticWebApps, false, false, DisplayName = "SWA Prod - No EnvVars - No Error")]
+ [DataRow(HostModeType.Production, EasyAuthType.StaticWebApps, true, false, DisplayName = "SWA Prod - EnvVars - No Error")]
+ public void TestProductionModeAppServiceEnvironmentCheck(HostModeType hostMode, EasyAuthType authType, bool setEnvVars, bool expectError)
+ {
+ // Clears or sets App Service Environment Variables based on test input.
+ Environment.SetEnvironmentVariable(AppServiceAuthenticationInfo.APPSERVICESAUTH_ENABLED_ENVVAR, setEnvVars ? "true" : null);
+ Environment.SetEnvironmentVariable(AppServiceAuthenticationInfo.APPSERVICESAUTH_IDENTITYPROVIDER_ENVVAR, setEnvVars ? "AzureActiveDirectory" : null);
+
+ RuntimeConfigProvider configProvider = TestHelper.GetRuntimeConfigProvider(MSSQL_ENVIRONMENT);
+ RuntimeConfig config = configProvider.GetRuntimeConfiguration();
+
+ // Setup configuration
+ AuthenticationConfig authenticationConfig = new(Provider: authType.ToString());
+ HostGlobalSettings customHostGlobalSettings = config.HostGlobalSettings with { Mode = hostMode, Authentication = authenticationConfig };
+ JsonElement serializedCustomHostGlobalSettings = JsonSerializer.SerializeToElement(customHostGlobalSettings, RuntimeConfig.SerializerOptions);
+ Dictionary customRuntimeSettings = new(config.RuntimeSettings);
+ customRuntimeSettings.Remove(GlobalSettingsType.Host);
+ customRuntimeSettings.Add(GlobalSettingsType.Host, serializedCustomHostGlobalSettings);
+ RuntimeConfig configWithCustomHostMode = config with { RuntimeSettings = customRuntimeSettings };
+
+ const string CUSTOM_CONFIG = "custom-config.json";
+ File.WriteAllText(path: CUSTOM_CONFIG, contents: JsonSerializer.Serialize(configWithCustomHostMode, RuntimeConfig.SerializerOptions));
+ string[] args = new[]
+ {
+ $"--ConfigFileName={CUSTOM_CONFIG}"
+ };
+
+ // This test only checks for startup errors, so no requests are sent to the test server.
+ try
+ {
+ using TestServer server = new(Program.CreateWebHostBuilder(args));
+ Assert.IsFalse(expectError, message: "Expected error faulting AppService config in production mode.");
+ }
+ catch (DataApiBuilderException ex)
+ {
+ Assert.IsTrue(expectError, message: ex.Message);
+ Assert.AreEqual(AppServiceAuthenticationInfo.APPSERVICE_PROD_MISSING_ENV_CONFIG, ex.Message);
+ }
+ }
+
///
/// Integration test that validates schema introspection requests fail
/// when allow-introspection is false in the runtime configuration.
diff --git a/src/Service/AuthenticationHelpers/AppServiceAuthenticationInformation.cs b/src/Service/AuthenticationHelpers/AppServiceAuthenticationInformation.cs
new file mode 100644
index 0000000000..eb4675a91c
--- /dev/null
+++ b/src/Service/AuthenticationHelpers/AppServiceAuthenticationInformation.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Azure.DataApiBuilder.Service.AuthenticationHelpers
+{
+ ///
+ /// Info about the App Services configuration on the host. This class is an abridged mirror of
+ /// Microsoft.Identity.Web's AppServicesAuthenticationInformation.cs helper class used to
+ /// detect whether the app is running in an Azure App Service environment.
+ ///
+ ///
+ public static class AppServiceAuthenticationInfo
+ {
+ ///
+ /// Environment variable key whose value represents whether AppService EasyAuth is enabled ("true" or "false").
+ ///
+ public const string APPSERVICESAUTH_ENABLED_ENVVAR = "WEBSITE_AUTH_ENABLED";
+ ///
+ /// Environment variable key whose value represents Identity Provider such as "AzureActiveDirectory"
+ ///
+ public const string APPSERVICESAUTH_IDENTITYPROVIDER_ENVVAR = "WEBSITE_AUTH_DEFAULT_PROVIDER";
+ ///
+ /// Error message used when AppService Authentication is configured in production mode in a non AppService Environment.
+ ///
+ public const string APPSERVICE_PROD_MISSING_ENV_CONFIG = "AppService environment not detected while runtime is in production mode.";
+ ///
+ /// Warning message logged when AppService environment not detected (applicable to development mode).
+ ///
+ public const string APPSERVICE_DEV_MISSING_ENV_CONFIG = "AppService environment not detected, EasyAuth authentication may not behave as expected.";
+
+ ///
+ /// Returns a best guess whether AppService is enabled in the environment by checking for
+ /// existence and value population of known AppService environment variables.
+ /// This check is determined to be "best guess" because environment variables could be
+ /// manually added or overridden.
+ /// This check's purpose is to help warn developers that an AppService environment is not detected
+ /// where the DataApiBuilder service is executing and DataApiBuilder is configured to use AppService
+ /// as the identity provider.
+ ///
+ public static bool AreExpectedAppServiceEnvVarsPresent()
+ {
+ string? appServiceEnabled = Environment.GetEnvironmentVariable(APPSERVICESAUTH_ENABLED_ENVVAR);
+ string? appServiceIdentityProvider = Environment.GetEnvironmentVariable(APPSERVICESAUTH_IDENTITYPROVIDER_ENVVAR);
+
+ if (string.IsNullOrEmpty(appServiceEnabled) || string.IsNullOrEmpty(appServiceIdentityProvider))
+ {
+ return false;
+ }
+
+ return appServiceEnabled.Equals(value: "true", comparisonType: StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/src/Service/AuthenticationHelpers/EasyAuthAuthenticationDefaults.cs b/src/Service/AuthenticationHelpers/EasyAuthAuthenticationDefaults.cs
index acf036e248..4d6cc322ae 100644
--- a/src/Service/AuthenticationHelpers/EasyAuthAuthenticationDefaults.cs
+++ b/src/Service/AuthenticationHelpers/EasyAuthAuthenticationDefaults.cs
@@ -1,12 +1,12 @@
namespace Azure.DataApiBuilder.Service.AuthenticationHelpers
{
///
- /// Default values related to StaticWebAppAuthentication handler.
+ /// Default values related to EasyAuthAuthentication handler.
///
public static class EasyAuthAuthenticationDefaults
{
///
- /// The default value used for StaticWebAppAuthenticationOptions.AuthenticationScheme.
+ /// The default value used for EasyAuthAuthenticationOptions.AuthenticationScheme.
///
public const string AUTHENTICATIONSCHEME = "EasyAuthAuthentication";
diff --git a/src/Service/Startup.cs b/src/Service/Startup.cs
index 5ea8f012f3..272c06c67d 100644
--- a/src/Service/Startup.cs
+++ b/src/Service/Startup.cs
@@ -383,7 +383,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, RuntimeC
///
/// The service collection where authentication services are added.
/// The provider used to load runtime configuration.
- private static void ConfigureAuthentication(IServiceCollection services, RuntimeConfigProvider runtimeConfigurationProvider)
+ private void ConfigureAuthentication(IServiceCollection services, RuntimeConfigProvider runtimeConfigurationProvider)
{
if (runtimeConfigurationProvider.TryGetRuntimeConfiguration(out RuntimeConfig? runtimeConfig) && runtimeConfig.AuthNConfig != null)
{
@@ -404,11 +404,27 @@ private static void ConfigureAuthentication(IServiceCollection services, Runtime
}
else if (runtimeConfig.IsEasyAuthAuthenticationProvider())
{
+ EasyAuthType easyAuthType = (EasyAuthType)Enum.Parse(typeof(EasyAuthType), runtimeConfig.AuthNConfig.Provider, ignoreCase: true);
+ bool isProductionMode = !runtimeConfigurationProvider.IsDeveloperMode();
+ bool appServiceEnvironmentDetected = AppServiceAuthenticationInfo.AreExpectedAppServiceEnvVarsPresent();
+
+ if (easyAuthType == EasyAuthType.AppService && !appServiceEnvironmentDetected)
+ {
+ if (isProductionMode)
+ {
+ throw new DataApiBuilderException(
+ message: AppServiceAuthenticationInfo.APPSERVICE_PROD_MISSING_ENV_CONFIG,
+ statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
+ subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
+ }
+ else
+ {
+ _logger.LogWarning(AppServiceAuthenticationInfo.APPSERVICE_DEV_MISSING_ENV_CONFIG);
+ }
+ }
+
services.AddAuthentication(EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication(
- (EasyAuthType)Enum.Parse(typeof(EasyAuthType),
- runtimeConfig.AuthNConfig.Provider,
- ignoreCase: true));
+ .AddEasyAuthAuthentication(easyAuthAuthenticationProvider: easyAuthType);
}
else if (runtimeConfigurationProvider.IsDeveloperMode() && runtimeConfig.IsAuthenticationSimulatorEnabled())
{