From e51a469050d64ab9def8bb4704ad9de13dbb9797 Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Fri, 25 Apr 2025 17:25:55 -0700 Subject: [PATCH 1/7] Support reading mode from env variables --- CHANGELOG.md | 2 ++ src/AzureAuth.Test/CommandAadTest.cs | 53 +++++++++++++++++++++++++++ src/AzureAuth/Commands/CommandAad.cs | 54 ++++++++++++++++++++++++---- src/AzureAuth/EnvVars.cs | 5 +++ 4 files changed, 108 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7fdafa6..095988cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Added support for reading auth mode from the environment variable `AZUREAUTH_MODE` for aad subcommands. ## [0.9.1] - 2024-12-09 ### Changed diff --git a/src/AzureAuth.Test/CommandAadTest.cs b/src/AzureAuth.Test/CommandAadTest.cs index 7aaf77f1..a2f29c00 100644 --- a/src/AzureAuth.Test/CommandAadTest.cs +++ b/src/AzureAuth.Test/CommandAadTest.cs @@ -444,6 +444,59 @@ public void TestEvaluateOptionsWithoutAliasValidCommandLineOptions() subject.TokenFetcherOptions.Should().BeEquivalentTo(expected); } + [Test] + public void TestEvaluateOptionsWithAuthModeFromCommandLineOptions() + { + CommandAad subject = this.serviceProvider.GetService(); + subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2"; + subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b"; + subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9"; + subject.AuthModes = new List() { AuthMode.DeviceCode }; + + this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns("Web,DeviceCode"); + subject.EvaluateOptions().Should().BeTrue(); + subject.AuthModes.Should().Contain(new[] { AuthMode.DeviceCode }); + } + + [Test] + public void TestEvaluateOptionsWithAuthModeFromEnvVar() + { + CommandAad subject = this.serviceProvider.GetService(); + subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2"; + subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b"; + subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9"; + + this.envMock.Setup(env => env.Get("AZUREAUTH_MODE")).Returns("Web,DeviceCode"); + subject.EvaluateOptions().Should().BeTrue(); + subject.AuthModes.Should().Contain(new[] { AuthMode.Web, AuthMode.DeviceCode }); + } + + [Test] + public void TestEvaluateOptionsWithNoAuthModeInEnvVarOrOptions() + { + CommandAad subject = this.serviceProvider.GetService(); + subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2"; + subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b"; + subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9"; + + this.envMock.Setup(env => env.Get(It.IsAny())).Returns((string)null); + subject.EvaluateOptions().Should().BeTrue(); + subject.AuthModes.Should().Contain(new[] { AuthMode.Default }); + } + + [Test] + public void TestEvaluateOptionsWithAuthModeFromInvalidEnvVars() + { + CommandAad subject = this.serviceProvider.GetService(); + subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2"; + subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b"; + subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9"; + + this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns("Invalid"); + subject.EvaluateOptions().Should().BeFalse(); + this.logTarget.Logs.Should().ContainMatch($"Invalid value specified for environment variable {EnvVars.AuthMode}*"); + } + /// /// The root path. /// diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 48130f48..5163dba8 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -60,12 +60,13 @@ public class CommandAad /// The help text for the option. /// #if PlatformWindows - public const string AuthModeHelperText = @"Authentication mode. Repeated invocations allowed. -[default: broker, then web] -[possible values: all, iwa, broker, web, devicecode]"; + public const string AuthModeHelperText = $"Authentication mode. Repeated invocations allowed.\n" + + $"[default: broker, then web]\n" + + $"[possible values: {AuthModeAllowedValues}]"; #else - public const string AuthModeHelperText = @"Authentication mode. Repeated invocations allowed. [default: web] -[possible values: all, web, devicecode]"; + public const string AuthModeHelperText = $"Authentication mode. Repeated invocations allowed.\n" + + $"[default: web]\n" + + $"[possible values: {AuthModeAllowedValues}]"; #endif /// @@ -83,6 +84,12 @@ public class CommandAad /// public static readonly TimeSpan GlobalTimeout = TimeSpan.FromMinutes(15); +#if PlatformWindows + private const string AuthModeAllowedValues = "all, iwa, broker, web, devicecode"; +#else + private const string AuthModeAllowedValues = "all, web, devicecode"; +#endif + private const string ResourceOption = "--resource"; private const string ClientOption = "--client"; @@ -179,7 +186,7 @@ public CommandAad(CommandExecuteEventData eventData, ITelemetryService telemetry /// Gets or sets the auth modes. /// [Option(ModeOption, AuthModeHelperText, CommandOptionType.MultipleValue)] - public IEnumerable AuthModes { get; set; } = new[] { AuthMode.Default }; + public IEnumerable AuthModes { get; set; } /// /// Gets or sets the output. @@ -272,6 +279,12 @@ public bool EvaluateOptions() } } + if (this.AuthModes is null && !this.TrySetAuthModeFromEnvOrDefault()) + { + this.logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {AuthModeAllowedValues}"); + return false; + } + // Handle Resource Shorthand for Default Scope if (evaluatedOptions.Scopes.IsNullOrEmpty() && !string.IsNullOrEmpty(evaluatedOptions.Resource)) { @@ -405,5 +418,34 @@ private int GetToken(IPublicClientAuth publicClientAuth) return 0; } + + /// + /// Sets the from the environment variable and sets a default if not set. + /// + /// The list of auth modes. + public bool TrySetAuthModeFromEnvOrDefault() + { + var authModesFromEnv = this.env.Get(EnvVars.AuthMode); + if (string.IsNullOrEmpty(authModesFromEnv)) + { + this.AuthModes = new[] { AuthMode.Default }; + } + + var result = new List(); + foreach(var val in authModesFromEnv.Split(',')) + { + if (Enum.TryParse(val, ignoreCase: true, out var mode)) + { + result.Add(mode); + } + else + { + return false; + } + } + + this.AuthModes = result; + return true; + } } } diff --git a/src/AzureAuth/EnvVars.cs b/src/AzureAuth/EnvVars.cs index 16f2d829..4b439e0b 100644 --- a/src/AzureAuth/EnvVars.cs +++ b/src/AzureAuth/EnvVars.cs @@ -46,6 +46,11 @@ public static class EnvVars /// public static readonly string AdoPat = $"{EnvVarPrefix}_ADO_PAT"; + /// + /// Name of the env var to get the Auth Mode. + /// + public static readonly string AuthMode = $"{EnvVarPrefix}_MODE"; + /// /// Name of the env var used to disable user based authentication modes. /// NOTE: This is a private variable and it is recommended to not rely on this variable. From b9f96566083ef5c66bb761a28ed5c537c3b7041c Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 10:25:38 -0700 Subject: [PATCH 2/7] update comment --- src/AzureAuth/Commands/CommandAad.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 5163dba8..9c967f1c 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -422,7 +422,7 @@ private int GetToken(IPublicClientAuth publicClientAuth) /// /// Sets the from the environment variable and sets a default if not set. /// - /// The list of auth modes. + /// True if authmode is set. public bool TrySetAuthModeFromEnvOrDefault() { var authModesFromEnv = this.env.Get(EnvVars.AuthMode); From 3c4b252745dc343809c8922c4a1a7c0180d22703 Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 11:22:04 -0700 Subject: [PATCH 3/7] early return when envvars is empty --- src/AzureAuth.Test/CommandAadTest.cs | 13 +++++++++++++ src/AzureAuth/Commands/CommandAad.cs | 1 + 2 files changed, 14 insertions(+) diff --git a/src/AzureAuth.Test/CommandAadTest.cs b/src/AzureAuth.Test/CommandAadTest.cs index a2f29c00..662c46e5 100644 --- a/src/AzureAuth.Test/CommandAadTest.cs +++ b/src/AzureAuth.Test/CommandAadTest.cs @@ -497,6 +497,19 @@ public void TestEvaluateOptionsWithAuthModeFromInvalidEnvVars() this.logTarget.Logs.Should().ContainMatch($"Invalid value specified for environment variable {EnvVars.AuthMode}*"); } + [Test] + public void TestEvaluateOptionsWithAuthModeFromEmptyEnvVars() + { + CommandAad subject = this.serviceProvider.GetService(); + subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2"; + subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b"; + subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9"; + + this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns(""); + subject.EvaluateOptions().Should().BeTrue(); + subject.AuthModes.Should().Contain(new[] { AuthMode.Default }); + } + /// /// The root path. /// diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 9c967f1c..cc4061cb 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -429,6 +429,7 @@ public bool TrySetAuthModeFromEnvOrDefault() if (string.IsNullOrEmpty(authModesFromEnv)) { this.AuthModes = new[] { AuthMode.Default }; + return true; } var result = new List(); From 538fb48bf69e21028757ac10bd04b68cf57fe435 Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 11:35:08 -0700 Subject: [PATCH 4/7] log env auth mode --- src/AzureAuth/Commands/CommandAad.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index cc4061cb..6a69f7b0 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -446,6 +446,7 @@ public bool TrySetAuthModeFromEnvOrDefault() } this.AuthModes = result; + this.eventData.Add($"env_{EnvVars.AuthMode}", authModesFromEnv); return true; } } From 88756b723fd13b090775a5363fab17e9ec4161a3 Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 11:54:40 -0700 Subject: [PATCH 5/7] string manipulation --- src/AzureAuth/Commands/CommandAad.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 6a69f7b0..58d90508 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -60,13 +60,13 @@ public class CommandAad /// The help text for the option. /// #if PlatformWindows - public const string AuthModeHelperText = $"Authentication mode. Repeated invocations allowed.\n" + - $"[default: broker, then web]\n" + - $"[possible values: {AuthModeAllowedValues}]"; + public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed. + [default: broker, then web] + [possible values: {AuthModeAllowedValues}]"; #else - public const string AuthModeHelperText = $"Authentication mode. Repeated invocations allowed.\n" + - $"[default: web]\n" + - $"[possible values: {AuthModeAllowedValues}]"; + public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed + [default: web] + [possible values: {AuthModeAllowedValues}]"; #endif /// From ef6a752ea1b39a8799a546cf7118e5da912fe15b Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 14:17:07 -0700 Subject: [PATCH 6/7] fix indendation --- src/AzureAuth/Commands/CommandAad.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 58d90508..6c88e00d 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -61,12 +61,12 @@ public class CommandAad /// #if PlatformWindows public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed. - [default: broker, then web] - [possible values: {AuthModeAllowedValues}]"; +[default: broker, then web] +[possible values: {AuthModeAllowedValues}]"; #else public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed - [default: web] - [possible values: {AuthModeAllowedValues}]"; +[default: web] +[possible values: {AuthModeAllowedValues}]"; #endif /// From 0c924874fcef221fb5fc440c88ea8c53dc6fefb1 Mon Sep 17 00:00:00 2001 From: "Manuha Vancha (from Dev Box)" Date: Mon, 28 Apr 2025 15:27:10 -0700 Subject: [PATCH 7/7] Fix failing unit tests --- src/AzureAuth.Test/CommandAadTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AzureAuth.Test/CommandAadTest.cs b/src/AzureAuth.Test/CommandAadTest.cs index 662c46e5..73df78ad 100644 --- a/src/AzureAuth.Test/CommandAadTest.cs +++ b/src/AzureAuth.Test/CommandAadTest.cs @@ -236,6 +236,7 @@ public void TestEvaluateOptionsProvidedAliasWithEnvVarConfig() // Specify config via env var this.envMock.Setup(e => e.Get("AZUREAUTH_CONFIG")).Returns(configFile); + this.envMock.Setup(env => env.Get(It.Is(key => key != "AZUREAUTH_CONFIG"))).Returns(key => null); // Specify a client override on the command line. subject.Client = clientOverride;