Skip to content

Commit 16a5d2e

Browse files
authored
Merge branch 'main' into changelog-link
2 parents b50b8bd + 6cf6a66 commit 16a5d2e

9 files changed

Lines changed: 205 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Added support for reading auth mode from the environment variable `AZUREAUTH_MODE`.
810

911
## [0.9.1] - 2024-12-09
1012
### Changed

src/AzureAuth.Test/CommandAadTest.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ public void TestEvaluateOptionsProvidedAliasWithEnvVarConfig()
236236

237237
// Specify config via env var
238238
this.envMock.Setup(e => e.Get("AZUREAUTH_CONFIG")).Returns(configFile);
239+
this.envMock.Setup(env => env.Get(It.Is<string>(key => key != "AZUREAUTH_CONFIG"))).Returns<string>(key => null);
239240

240241
// Specify a client override on the command line.
241242
subject.Client = clientOverride;
@@ -444,6 +445,72 @@ public void TestEvaluateOptionsWithoutAliasValidCommandLineOptions()
444445
subject.TokenFetcherOptions.Should().BeEquivalentTo(expected);
445446
}
446447

448+
[Test]
449+
public void TestEvaluateOptionsWithAuthModeFromCommandLineOptions()
450+
{
451+
CommandAad subject = this.serviceProvider.GetService<CommandAad>();
452+
subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2";
453+
subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b";
454+
subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9";
455+
subject.AuthModes = new List<AuthMode>() { AuthMode.DeviceCode };
456+
457+
this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns("Web,DeviceCode");
458+
subject.EvaluateOptions().Should().BeTrue();
459+
subject.AuthModes.Should().Contain(new[] { AuthMode.DeviceCode });
460+
}
461+
462+
[Test]
463+
public void TestEvaluateOptionsWithAuthModeFromEnvVar()
464+
{
465+
CommandAad subject = this.serviceProvider.GetService<CommandAad>();
466+
subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2";
467+
subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b";
468+
subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9";
469+
470+
this.envMock.Setup(env => env.Get("AZUREAUTH_MODE")).Returns("Web,DeviceCode");
471+
subject.EvaluateOptions().Should().BeTrue();
472+
subject.AuthModes.Should().Contain(new[] { AuthMode.Web, AuthMode.DeviceCode });
473+
}
474+
475+
[Test]
476+
public void TestEvaluateOptionsWithNoAuthModeInEnvVarOrOptions()
477+
{
478+
CommandAad subject = this.serviceProvider.GetService<CommandAad>();
479+
subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2";
480+
subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b";
481+
subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9";
482+
483+
this.envMock.Setup(env => env.Get(It.IsAny<string>())).Returns((string)null);
484+
subject.EvaluateOptions().Should().BeTrue();
485+
subject.AuthModes.Should().Contain(new[] { AuthMode.Default });
486+
}
487+
488+
[Test]
489+
public void TestEvaluateOptionsWithAuthModeFromInvalidEnvVars()
490+
{
491+
CommandAad subject = this.serviceProvider.GetService<CommandAad>();
492+
subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2";
493+
subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b";
494+
subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9";
495+
496+
this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns("Invalid");
497+
subject.EvaluateOptions().Should().BeFalse();
498+
this.logTarget.Logs.Should().ContainMatch($"Invalid value specified for environment variable {EnvVars.AuthMode}*");
499+
}
500+
501+
[Test]
502+
public void TestEvaluateOptionsWithAuthModeFromEmptyEnvVars()
503+
{
504+
CommandAad subject = this.serviceProvider.GetService<CommandAad>();
505+
subject.Resource = "f0e8d801-3a50-48fd-b2da-6476d6e832a2";
506+
subject.Client = "e19f71ed-3b14-448d-9346-9eff9753646b";
507+
subject.Tenant = "9f6227ee-3d14-473e-8bed-1281171ef8c9";
508+
509+
this.envMock.Setup(env => env.Get(EnvVars.AuthMode)).Returns("");
510+
subject.EvaluateOptions().Should().BeTrue();
511+
subject.AuthModes.Should().Contain(new[] { AuthMode.Default });
512+
}
513+
447514
/// <summary>
448515
/// The root path.
449516
/// </summary>

src/AzureAuth.Test/IEnvExtensionsTest.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,26 @@ namespace AzureAuth.Test
66
using FluentAssertions;
77

88
using Microsoft.Authentication.AzureAuth;
9+
using Microsoft.Authentication.MSALWrapper;
10+
using Microsoft.Authentication.TestHelper;
11+
using Microsoft.Extensions.Logging;
912
using Microsoft.Office.Lasso.Interfaces;
10-
13+
using Microsoft.Office.Lasso.Telemetry;
1114
using Moq;
12-
15+
using NLog.Targets;
1316
using NUnit.Framework;
1417

1518
public class IEnvExtensionsTest
1619
{
1720
private Mock<IEnv> envMock;
21+
private ILogger logger;
22+
private MemoryTarget logTarget;
1823

1924
[SetUp]
2025
public void SetUp()
2126
{
2227
this.envMock = new Mock<IEnv>();
28+
(this.logger, this.logTarget) = MemoryLogger.Create();
2329
}
2430

2531
[TestCase("1", true)]
@@ -51,5 +57,44 @@ public void InteractiveAuth_IsEnabledIfEnvVarsAreNotSet()
5157
this.envMock.Setup(env => env.Get(It.IsAny<string>())).Returns((string)null);
5258
IEnvExtensions.InteractiveAuthDisabled(this.envMock.Object).Should().BeFalse();
5359
}
60+
61+
[Test]
62+
public void ReadAuthModeFromEnvOrSetDefault_ReturnsDefault_WhenEnvVarIsEmpty()
63+
{
64+
// Arrange
65+
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns(string.Empty);
66+
67+
// Act
68+
var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object);
69+
70+
// Assert
71+
result.Should().BeEquivalentTo(new[] { AuthMode.Default });
72+
}
73+
74+
[Test]
75+
public void ReadAuthModeFromEnvOrSetDefault_ReturnsParsedAuthModes_WhenEnvVarIsValid()
76+
{
77+
// Arrange
78+
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns("Web,DeviceCode");
79+
80+
// Act
81+
var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object);
82+
83+
// Assert
84+
result.Should().BeEquivalentTo(new[] { AuthMode.Web, AuthMode.DeviceCode });
85+
}
86+
87+
[Test]
88+
public void ReadAuthModeFromEnvOrSetDefault_ReturnsEmpty_WhenEnvVarIsInvalid()
89+
{
90+
// Arrange
91+
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns("InvalidMode");
92+
93+
// Act
94+
var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object);
95+
96+
// Assert
97+
result.Should().BeEmpty();
98+
}
5499
}
55100
}

src/AzureAuth/Commands/Ado/CommandPat.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ namespace Microsoft.Authentication.AzureAuth.Commands.Ado
77
using System.Collections.Generic;
88
using System.Collections.Immutable;
99
using System.IO;
10+
using System.Linq;
1011
using McMaster.Extensions.CommandLineUtils;
1112

1213
using Microsoft.Authentication.AdoPat;
1314
using Microsoft.Authentication.AzureAuth.Ado;
1415
using Microsoft.Authentication.MSALWrapper;
1516
using Microsoft.Extensions.Logging;
1617
using Microsoft.Identity.Client.Extensions.Msal;
18+
using Microsoft.Office.Lasso.Interfaces;
1719
using Microsoft.Office.Lasso.Telemetry;
1820
using Microsoft.VisualStudio.Services.DelegatedAuthorization;
1921
using Microsoft.VisualStudio.Services.OAuth;
@@ -92,7 +94,7 @@ private enum OutputMode
9294
private string Tenant { get; set; } = AzureAuth.Ado.Constants.Tenant.Microsoft;
9395

9496
[Option(CommandAad.ModeOption, CommandAad.AuthModeHelperText, CommandOptionType.MultipleValue)]
95-
private IEnumerable<AuthMode> AuthModes { get; set; } = new[] { AuthMode.Default };
97+
private IEnumerable<AuthMode> AuthModes { get; set; }
9698

9799
[Option(CommandAad.DomainOption, $"{CommandAad.DomainHelpText}\n[default: {AzureAuth.Ado.Constants.PreferredDomain}]", CommandOptionType.SingleValue)]
98100
private string Domain { get; set; } = AzureAuth.Ado.Constants.PreferredDomain;
@@ -120,14 +122,23 @@ private ImmutableSortedSet<string> Scopes
120122
/// <param name="logger">The <see cref="ILogger{T}"/> instance that is used for logging.</param>
121123
/// <param name="publicClientAuth">An <see cref="IPublicClientAuth"/>.</param>
122124
/// <param name="eventData">Lasso injected command event data.</param>
125+
/// <param name="env">An <see cref="IEnv"/> to use.</param>
123126
/// <returns>An integer status code. 0 for success and non-zero for failure.</returns>
124-
public int OnExecute(ILogger<CommandPat> logger, IPublicClientAuth publicClientAuth, CommandExecuteEventData eventData)
127+
public int OnExecute(ILogger<CommandPat> logger, IPublicClientAuth publicClientAuth, CommandExecuteEventData eventData, IEnv env)
125128
{
126129
if (!this.ValidOptions(logger))
127130
{
128131
return 1;
129132
}
130133

134+
// If command line options for mode are not specified, then use the environment variables.
135+
this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault();
136+
if (!this.AuthModes.Any())
137+
{
138+
logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}");
139+
return 1;
140+
}
141+
131142
var accessToken = this.AccessToken(publicClientAuth, eventData);
132143
if (accessToken == null)
133144
{

src/AzureAuth/Commands/Ado/CommandToken.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Microsoft.Authentication.AzureAuth.Commands.Ado
55
{
66
using System;
77
using System.Collections.Generic;
8-
8+
using System.Linq;
99
using McMaster.Extensions.CommandLineUtils;
1010

1111
using Microsoft.Authentication.AzureAuth.Ado;
@@ -52,7 +52,7 @@ public enum OutputMode
5252
private string Tenant { get; set; } = AzureAuth.Ado.Constants.Tenant.Microsoft;
5353

5454
[Option(CommandAad.ModeOption, CommandAad.AuthModeHelperText, CommandOptionType.MultipleValue)]
55-
private IEnumerable<AuthMode> AuthModes { get; set; } = new[] { AuthMode.Default };
55+
private IEnumerable<AuthMode> AuthModes { get; set; }
5656

5757
[Option(CommandAad.DomainOption, Description = DomainOptionDescription)]
5858
private string Domain { get; set; } = AzureAuth.Ado.Constants.PreferredDomain;
@@ -98,6 +98,14 @@ public int OnExecute(ILogger<CommandToken> logger, IEnv env, ITelemetryService t
9898
return 0;
9999
}
100100

101+
// If command line options for mode are not specified, then use the environment variables.
102+
this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault();
103+
if (!this.AuthModes.Any())
104+
{
105+
logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}");
106+
return 1;
107+
}
108+
101109
// If no PAT then use AAD AT.
102110
TokenResult token = publicClientAuth.Token(
103111
AzureAuth.Ado.AuthParameters.AdoParameters(this.Tenant),

src/AzureAuth/Commands/CommandAad.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ public class CommandAad
6060
/// The help text for the <see cref="ModeOption"/> option.
6161
/// </summary>
6262
#if PlatformWindows
63-
public const string AuthModeHelperText = @"Authentication mode. Repeated invocations allowed.
63+
public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed.
6464
[default: broker, then web]
65-
[possible values: all, iwa, broker, web, devicecode]";
65+
[possible values: {AuthModeAllowedValues}]";
6666
#else
67-
public const string AuthModeHelperText = @"Authentication mode. Repeated invocations allowed. [default: web]
68-
[possible values: all, web, devicecode]";
67+
public const string AuthModeHelperText = $@"Authentication mode. Repeated invocations allowed
68+
[default: web]
69+
[possible values: {AuthModeAllowedValues}]";
6970
#endif
7071

7172
/// <summary>
@@ -83,6 +84,15 @@ public class CommandAad
8384
/// </summary>
8485
public static readonly TimeSpan GlobalTimeout = TimeSpan.FromMinutes(15);
8586

87+
/// <summary>
88+
/// The allowed values for the <see cref="AuthMode"/> option.
89+
/// </summary>
90+
#if PlatformWindows
91+
public const string AuthModeAllowedValues = "all, iwa, broker, web, devicecode";
92+
#else
93+
public const string AuthModeAllowedValues = "all, web, devicecode";
94+
#endif
95+
8696
private const string ResourceOption = "--resource";
8797
private const string ClientOption = "--client";
8898

@@ -179,7 +189,7 @@ public CommandAad(CommandExecuteEventData eventData, ITelemetryService telemetry
179189
/// Gets or sets the auth modes.
180190
/// </summary>
181191
[Option(ModeOption, AuthModeHelperText, CommandOptionType.MultipleValue)]
182-
public IEnumerable<AuthMode> AuthModes { get; set; } = new[] { AuthMode.Default };
192+
public IEnumerable<AuthMode> AuthModes { get; set; }
183193

184194
/// <summary>
185195
/// Gets or sets the output.
@@ -272,6 +282,14 @@ public bool EvaluateOptions()
272282
}
273283
}
274284

285+
// If command line options for mode are not specified, then use the environment variables.
286+
this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault();
287+
if (!this.AuthModes.Any())
288+
{
289+
this.logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}");
290+
return false;
291+
}
292+
275293
// Handle Resource Shorthand for Default Scope
276294
if (evaluatedOptions.Scopes.IsNullOrEmpty() && !string.IsNullOrEmpty(evaluatedOptions.Resource))
277295
{

src/AzureAuth/EnvVars.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public static class EnvVars
4646
/// </summary>
4747
public static readonly string AdoPat = $"{EnvVarPrefix}_ADO_PAT";
4848

49+
/// <summary>
50+
/// Name of the env var to get the Auth Mode.
51+
/// </summary>
52+
public static readonly string AuthMode = $"{EnvVarPrefix}_MODE";
53+
4954
/// <summary>
5055
/// Name of the env var used to disable user based authentication modes.
5156
/// NOTE: This is a private variable and it is recommended to not rely on this variable.

src/AzureAuth/IEnvExtensions.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
namespace Microsoft.Authentication.AzureAuth
55
{
6+
using Microsoft.Authentication.MSALWrapper;
67
using Microsoft.Office.Lasso.Interfaces;
8+
using Microsoft.Office.Lasso.Telemetry;
9+
using System.Collections.Generic;
10+
using System;
711

812
/// <summary>
913
/// Extension methods to Lasso's <see cref="IEnv"/> interface.
@@ -22,5 +26,38 @@ public static bool InteractiveAuthDisabled(this IEnv env)
2226
return !string.IsNullOrEmpty(env.Get(EnvVars.NoUser)) ||
2327
string.Equals(CorextPositiveValue, env.Get(EnvVars.CorextNonInteractive));
2428
}
29+
30+
/// <summary>
31+
/// Get the auth modes from the environment or set the default.
32+
/// </summary>
33+
/// <param name="env">The <see cref="IEnv"/> to use.</param>
34+
/// <param name="eventData">Event data to add the auth mode to.</param>
35+
/// <returns>AuthModes.</returns>
36+
public static IEnumerable<AuthMode> ReadAuthModeFromEnvOrSetDefault(this IEnv env)
37+
{
38+
var authModesFromEnv = env.Get(EnvVars.AuthMode);
39+
40+
// If auth modes are not specified in the environment, then return the default.
41+
if (string.IsNullOrEmpty(authModesFromEnv))
42+
{
43+
return new[] { AuthMode.Default };
44+
}
45+
46+
var result = new List<AuthMode>();
47+
foreach (var val in authModesFromEnv.Split(','))
48+
{
49+
if (Enum.TryParse<AuthMode>(val, ignoreCase: true, out var mode))
50+
{
51+
result.Add(mode);
52+
}
53+
else
54+
{
55+
// If the environment variable is not a valid auth mode, then return an empty list.
56+
return new List<AuthMode>();
57+
}
58+
}
59+
60+
return result;
61+
}
2562
}
2663
}

src/AzureAuth/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ private static void Main(string[] args)
6868
EnvVars.CloudBuild,
6969
EnvVars.NoUser,
7070
EnvVars.CorextNonInteractive,
71+
EnvVars.AuthMode,
7172
};
7273

7374
TelemetryConfig telemetryConfig = new TelemetryConfig(

0 commit comments

Comments
 (0)