diff --git a/.azure-pipelines/generate-auth-module-template.yml b/.azure-pipelines/generate-auth-module-template.yml index 0600456da0a..64a9ff49617 100644 --- a/.azure-pipelines/generate-auth-module-template.yml +++ b/.azure-pipelines/generate-auth-module-template.yml @@ -21,6 +21,14 @@ parameters: displayName: 'Build Number' type: string default: $[format('{0:yyMMddHH}', pipeline.startTime)] + - name: AZURESUBSCRIPTION + default: "Microsoft Graph Build Agents (Win+Lin)" + displayName: Azure Subscription + + - name: KEYVAULT + default: "msgraph-build-keyvault" + displayName: Build Key vault + jobs: - job: MsGraphPSSDKAuthModuleGeneration @@ -29,6 +37,26 @@ jobs: steps: - template: ./install-tools-template.yml + - task: AzureKeyVault@1 + inputs: + azureSubscription: $(AZURESUBSCRIPTION) + KeyVaultName: $(KEYVAULT) + SecretsFilter: '*' + RunAsPreJob: true + + - task: PowerShell@2 + displayName: 'Install Test Certificate' + inputs: + targetType: 'inline' + script: | + $kvSecretBytes = [System.Convert]::FromBase64String('$(MsGraphPSSDKCertificate)') + $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection + $certCollection.Import($kvSecretBytes,$null,[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My", "CurrentUser") + $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) + $store.AddRange($certCollection) + $store.Close() + - task: PowerShell@2 displayName: 'Generate and Build Auth Module' inputs: diff --git a/.azure-pipelines/integrated-pipeline.yml b/.azure-pipelines/integrated-pipeline.yml index dc2af0bc751..31a22510e42 100644 --- a/.azure-pipelines/integrated-pipeline.yml +++ b/.azure-pipelines/integrated-pipeline.yml @@ -101,6 +101,8 @@ stages: AUTH_MODULE_PATH: $(AUTH_MODULE_PATH) EnableSigning: true BUILDNUMBER: $(BUILDNUMBER) + KEYVAULT: $(KEYVAULT) + AZURESUBSCRIPTION: $(AZURESUBSCRIPTION) - stage: GenerateBetaModules displayName: 'Generate Beta Modules (Microsoft.Graph.*)' diff --git a/src/Authentication/Authentication.Core/Authenticator.cs b/src/Authentication/Authentication.Core/Authenticator.cs index fe23f3a1a1c..f007ba4011d 100644 --- a/src/Authentication/Authentication.Core/Authenticator.cs +++ b/src/Authentication/Authentication.Core/Authenticator.cs @@ -9,6 +9,7 @@ namespace Microsoft.Graph.Authentication.Core using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.Helpers; using Microsoft.Identity.Client; + using System; using System.Collections.Generic; using System.Globalization; @@ -28,24 +29,41 @@ public static class Authenticator /// The to authenticate. /// Whether or not to force refresh a token if one exists. /// The cancellation token. + /// Callback to report FallBack to DeviceCode Authentication /// - public static async Task AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken) + public static async Task AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken, Action fallBackWarning = null) { - try + // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. + var authProvider = AuthenticationHelpers.GetAuthProvider(authContext); + IClientApplicationBase clientApplication = null; + switch (authContext.AuthProviderType) { - // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. - IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); - IClientApplicationBase clientApplication = null; - if (authContext.AuthType == AuthenticationType.Delegated) - { + case AuthProviderType.DeviceCodeProvider: + case AuthProviderType.DeviceCodeProviderFallBack: clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; - } - if (authContext.AuthType == AuthenticationType.AppOnly) - { - clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; - } - - // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. + break; + case AuthProviderType.InteractiveAuthenticationProvider: + { + var interactiveProvider = (authProvider as InteractiveAuthenticationProvider).ClientApplication; + //When User is not Interactive, Pre-Emptively Fallback and warn, to DeviceCode + if (!interactiveProvider.IsUserInteractive()) + { + authContext.AuthProviderType = AuthProviderType.DeviceCodeProviderFallBack; + fallBackWarning?.Invoke(); + var fallBackAuthContext= await AuthenticateAsync(authContext, forceRefresh, cancellationToken, fallBackWarning); + return fallBackAuthContext; + } + break; + } + case AuthProviderType.ClientCredentialProvider: + { + clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; + break; + } + } + try + { + // Incremental scope consent without re-instantiating the auth provider. We will use provided instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary @@ -81,18 +99,27 @@ public static async Task AuthenticateAsync(IAuthContext authContex } catch (AuthenticationException authEx) { - if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) + //Interactive Authentication Failure: Could Not Open Browser, fallback to DeviceAuth + if (IsUnableToOpenWebPageError(authEx)) { - // DeviceCodeTimeout - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.DeviceCodeTimeout, - Constants.MaxDeviceCodeTimeOut)); + authContext.AuthProviderType = AuthProviderType.DeviceCodeProviderFallBack; + //ReAuthenticate using DeviceCode as fallback. + var fallBackAuthContext = await AuthenticateAsync(authContext, forceRefresh, cancellationToken); + //Indicate that this was a Fallback + if (fallBackWarning != null && fallBackAuthContext.AuthProviderType == AuthProviderType.DeviceCodeProviderFallBack) + { + fallBackWarning(); + } + return fallBackAuthContext; } - else + // DeviceCode Authentication Failure: Timeout + if (authEx.InnerException is TaskCanceledException && cancellationToken.IsCancellationRequested) { - throw authEx.InnerException ?? authEx; + // DeviceCodeTimeout + throw new Exception(string.Format(CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } + //Something Unknown Went Wrong + throw authEx.InnerException ?? authEx; } catch (Exception ex) { @@ -108,5 +135,12 @@ public static void LogOut(IAuthContext authContext) { AuthenticationHelpers.Logout(authContext); } + + private static bool IsUnableToOpenWebPageError(Exception exception) + { + return exception.InnerException is MsalClientException clientException && + clientException?.ErrorCode == MsalError.LinuxXdgOpen || + (exception.Message?.ToLower()?.Contains("unable to open a web page") ?? false); + } } -} +} \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs b/src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs index 6e52994179d..11994fd0b35 100644 --- a/src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs +++ b/src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs @@ -18,7 +18,14 @@ public enum ContextScope Process, CurrentUser } - + public enum AuthProviderType + { + InteractiveAuthenticationProvider, + DeviceCodeProvider, + DeviceCodeProviderFallBack, + ClientCredentialProvider, + UserProvidedToken + } public interface IAuthContext { string ClientId { get; set; } @@ -26,6 +33,7 @@ public interface IAuthContext string CertificateThumbprint { get; set; } string[] Scopes { get; set; } AuthenticationType AuthType { get; set; } + AuthProviderType AuthProviderType { get; set; } string CertificateName { get; set; } string Account { get; set; } string AppName { get; set; } diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index 9832da8843a..7eeb4c233bf 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -3,15 +3,16 @@ netstandard2.0;netcoreapp2.1;net461 Microsoft.Graph.PowerShell.Authentication.Core - 1.4.2 + 1.4.3 - - - + + + + diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 1a5d80ca604..644d37302c5 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -67,28 +67,37 @@ public static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) { case AuthenticationType.Delegated: { + //Specify Default RedirectUri + //https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/MSAL.NET-uses-web-browser IPublicClientApplication publicClientApp = PublicClientApplicationBuilder - .Create(authContext.ClientId) - .WithTenantId(authContext.TenantId) - .WithAuthority(authorityUrl) - .WithClientCapabilities(new[] { "cp1" }) - .Build(); - + .Create(authContext.ClientId) + .WithTenantId(authContext.TenantId) + .WithAuthority(authorityUrl) + .WithClientCapabilities(new[] { "cp1" }) + .WithDefaultRedirectUri() + .Build(); ConfigureTokenCache(publicClientApp.UserTokenCache, authContext); - authProvider = new DeviceCodeProvider(publicClientApp, authContext.Scopes, async (result) => + switch (authContext.AuthProviderType) { - await Console.Out.WriteLineAsync(result.Message); - }); + case AuthProviderType.DeviceCodeProvider: + case AuthProviderType.DeviceCodeProviderFallBack: + authProvider = new DeviceCodeProvider(publicClientApp, authContext.Scopes, + async result => { await Console.Out.WriteLineAsync(result.Message); }); + break; + case AuthProviderType.InteractiveAuthenticationProvider: + authProvider = new InteractiveAuthenticationProvider(publicClientApp, authContext.Scopes); + break; + } break; } case AuthenticationType.AppOnly: { IConfidentialClientApplication confidentialClientApp = ConfidentialClientApplicationBuilder - .Create(authContext.ClientId) - .WithTenantId(authContext.TenantId) - .WithAuthority(authorityUrl) - .WithCertificate(GetCertificate(authContext)) - .Build(); + .Create(authContext.ClientId) + .WithTenantId(authContext.TenantId) + .WithAuthority(authorityUrl) + .WithCertificate(GetCertificate(authContext)) + .Build(); ConfigureTokenCache(confidentialClientApp.AppTokenCache, authContext); string graphBaseUrl = GraphSession.Instance.Environment?.GraphEndpoint ?? "https://graph.microsoft.com"; @@ -96,15 +105,13 @@ public static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) break; } case AuthenticationType.UserProvidedAccessToken: + authProvider = new DelegateAuthenticationProvider(requestMessage => { - authProvider = new DelegateAuthenticationProvider((requestMessage) => - { - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", - new NetworkCredential(string.Empty, GraphSession.Instance.UserProvidedToken).Password); - return Task.CompletedTask; - }); - break; - } + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", + new NetworkCredential(string.Empty, GraphSession.Instance.UserProvidedToken).Password); + return Task.CompletedTask; + }); + break; } return authProvider; } @@ -255,4 +262,4 @@ private static X509Certificate2 GetCertificateByName(string certificateName) return xCertificate; } } -} +} \ No newline at end of file diff --git a/src/Authentication/Authentication.Test/Helpers/AuthenticationHelpersTests.cs b/src/Authentication/Authentication.Test/Helpers/AuthenticationHelpersTests.cs index 3bf8906e43d..359bd51f163 100644 --- a/src/Authentication/Authentication.Test/Helpers/AuthenticationHelpersTests.cs +++ b/src/Authentication/Authentication.Test/Helpers/AuthenticationHelpersTests.cs @@ -48,14 +48,36 @@ public async Task ShouldUseDelegateAuthProviderWhenUserAccessTokenIsProvidedAsyn } [Fact] - public void ShouldUseDeviceCodeProviderWhenDelegatedContextIsProvided() + public void ShouldUseDeviceCodeWhenSpecifiedByUser() { // Arrange AuthContext delegatedAuthContext = new AuthContext { AuthType = AuthenticationType.Delegated, - Scopes = new string[] { "User.Read" }, - ContextScope = ContextScope.Process + Scopes = new[] { "User.Read" }, + ContextScope = ContextScope.Process, + AuthProviderType = AuthProviderType.DeviceCodeProvider + }; + + // Act + IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(delegatedAuthContext); + + // Assert + Assert.IsType(authProvider); + + // reset static instance. + GraphSession.Reset(); + } + [Fact] + public void ShouldUseDeviceCodeWhenFallback() + { + // Arrange + AuthContext delegatedAuthContext = new AuthContext + { + AuthType = AuthenticationType.Delegated, + Scopes = new[] { "User.Read" }, + ContextScope = ContextScope.Process, + AuthProviderType = AuthProviderType.DeviceCodeProviderFallBack }; // Act @@ -67,6 +89,26 @@ public void ShouldUseDeviceCodeProviderWhenDelegatedContextIsProvided() // reset static instance. GraphSession.Reset(); } + [Fact] + public void ShouldUseInteractiveProviderWhenDelegated() + { + // Arrange + AuthContext delegatedAuthContext = new AuthContext + { + AuthType = AuthenticationType.Delegated, + Scopes = new[] { "User.Read" }, + ContextScope = ContextScope.Process + }; + + // Act + IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(delegatedAuthContext); + + // Assert + Assert.IsType(authProvider); + + // reset static instance. + GraphSession.Reset(); + } #if NETCORE [Fact] diff --git a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs index 4de46081237..fef860afea4 100644 --- a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs +++ b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs @@ -1,28 +1,31 @@ // ------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ + +using AsyncHelpers = Microsoft.Graph.PowerShell.Authentication.Helpers.AsyncHelpers; + namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets { using System; + using System.Collections; using System.Collections.Generic; using System.Linq; using System.Management.Automation; - using System.Threading; - using System.Threading.Tasks; using System.Net; - using System.Collections; using System.Security.Cryptography.X509Certificates; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Graph.Authentication.Core; + using Microsoft.Graph.PowerShell.Authentication.Common; using Microsoft.Graph.PowerShell.Authentication.Helpers; + using Microsoft.Graph.PowerShell.Authentication.Interfaces; using Microsoft.Graph.PowerShell.Authentication.Models; - - using Interfaces; - using Common; - - using static Helpers.AsyncHelpers; - using Microsoft.Graph.Authentication.Core; + using Microsoft.Graph.PowerShell.Authentication.Properties; using Microsoft.Graph.PowerShell.Authentication.Utilities; + using static AsyncHelpers; + [Cmdlet(VerbsCommunications.Connect, "MgGraph", DefaultParameterSetName = Constants.UserParameterSet)] [Alias("Connect-Graph")] public class ConnectMgGraph : PSCmdlet, IModuleAssemblyInitializer, IModuleAssemblyCleanup @@ -87,12 +90,27 @@ public class ConnectMgGraph : PSCmdlet, IModuleAssemblyInitializer, IModuleAssem [Alias("EnvironmentName", "NationalCloud")] public string Environment { get; set; } + [Parameter(ParameterSetName = Constants.UserParameterSet, + Mandatory = false, HelpMessage = "Use device code authentication instead of a browser control")] + [Alias("DeviceCode", "DeviceAuth", "Device")] + public SwitchParameter UseDeviceAuthentication { get; set; } + /// + /// Wait for .NET debugger to attach + /// + [Parameter(Mandatory = false, + DontShow = true, + HelpMessage = "Wait for .NET debugger to attach")] + public SwitchParameter Break { get; set; } + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private IGraphEnvironment environment; - protected override void BeginProcessing() { + if (Break) + { + this.Break(); + } base.BeginProcessing(); ValidateParameters(); @@ -109,12 +127,6 @@ protected override void BeginProcessing() environment = GraphEnvironment.BuiltInEnvironments[GraphEnvironmentConstants.EnvironmentName.Global]; } } - - protected override void EndProcessing() - { - base.EndProcessing(); - } - protected override void ProcessRecord() { base.ProcessRecord(); @@ -170,9 +182,10 @@ private async Task ProcessRecordAsync() _cancellationTokenSource.CancelAfter(authTimeout); authContext.AuthType = AuthenticationType.Delegated; string[] processedScopes = ProcessScopes(Scopes); - authContext.Scopes = processedScopes.Length == 0 ? new string[] { "User.Read" } : processedScopes; + authContext.Scopes = processedScopes.Length == 0 ? new[] { "User.Read" } : processedScopes; // Default to CurrentUser but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.CurrentUser; + authContext.AuthProviderType = UseDeviceAuthentication ? AuthProviderType.DeviceCodeProvider : AuthProviderType.InteractiveAuthenticationProvider; } break; case Constants.AppParameterSet: @@ -184,6 +197,7 @@ private async Task ProcessRecordAsync() authContext.Certificate = Certificate; // Default to Process but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.Process; + authContext.AuthProviderType = AuthProviderType.ClientCredentialProvider; } break; case Constants.AccessTokenParameterSet: @@ -192,24 +206,25 @@ private async Task ProcessRecordAsync() authContext.ContextScope = ContextScope.Process; // Store user provided access token to a session object. GraphSession.Instance.UserProvidedToken = new NetworkCredential(string.Empty, AccessToken).SecurePassword; + authContext.AuthProviderType = AuthProviderType.UserProvidedToken; } break; } try { - // Save auth context to session state. - GraphSession.Instance.AuthContext = await Authenticator.AuthenticateAsync(authContext, ForceRefresh, _cancellationTokenSource.Token); + + GraphSession.Instance.AuthContext = await Authenticator.AuthenticateAsync(authContext, ForceRefresh, + _cancellationTokenSource.Token, + () => { WriteWarning(Resources.DeviceCodeFallback); }); } - catch(Exception ex) + catch (Exception ex) { throw ex; } - WriteObject("Welcome To Microsoft Graph!"); } } - protected override void StopProcessing() { _cancellationTokenSource.Cancel(); @@ -254,7 +269,7 @@ private void ValidateParameters() } // Certificate Thumbprint, Name or Actual Certificate - if (string.IsNullOrEmpty(CertificateThumbprint) && string.IsNullOrEmpty(CertificateName) && this.Certificate == null) + if (string.IsNullOrEmpty(CertificateThumbprint) && string.IsNullOrEmpty(CertificateName) && Certificate == null) { this.ThrowParameterError($"{nameof(CertificateThumbprint)} or {nameof(CertificateName)} or {nameof(Certificate)}"); } @@ -305,4 +320,4 @@ public void OnRemove(PSModuleInfo psModuleInfo) DependencyAssemblyResolver.Reset(); } } -} +} \ No newline at end of file diff --git a/src/Authentication/Authentication/Models/AuthContext.cs b/src/Authentication/Authentication/Models/AuthContext.cs index 5a7205e3a57..d52fbb312c2 100644 --- a/src/Authentication/Authentication/Models/AuthContext.cs +++ b/src/Authentication/Authentication/Models/AuthContext.cs @@ -13,6 +13,7 @@ public class AuthContext: IAuthContext public string CertificateThumbprint { get; set; } public string[] Scopes { get; set; } public AuthenticationType AuthType { get; set; } + public AuthProviderType AuthProviderType { get; set; } public string CertificateName { get; set; } public string Account { get; set; } public string AppName { get; set; } @@ -24,4 +25,4 @@ public AuthContext() ClientId = PowerShellClientId; } } -} +} \ No newline at end of file diff --git a/src/Authentication/Authentication/Properties/Resources.Designer.cs b/src/Authentication/Authentication/Properties/Resources.Designer.cs index 6d7e3e333ae..460051f0d51 100644 --- a/src/Authentication/Authentication/Properties/Resources.Designer.cs +++ b/src/Authentication/Authentication/Properties/Resources.Designer.cs @@ -123,6 +123,15 @@ internal static string ContentTypeExceptionErrorMessage { } } + /// + /// Looks up a localized string similar to Interactive authentication is not supported in this session, Falling Back to DeviceCode. Future versions will not automatically fallback to DeviceCode.. + /// + internal static string DeviceCodeFallback { + get { + return ResourceManager.GetString("DeviceCodeFallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Path '{0}' resolves to a directory. Specify a path including a file name, and then retry the command.. /// @@ -204,6 +213,15 @@ internal static string InferredFileNameVerboseMessage { } } + /// + /// Looks up a localized string similar to Interactive authentication is not supported in this session, please run cmdlet 'Connect-MgGraph -UseDeviceAuthentication'.. + /// + internal static string InteractiveAuthNotSupported { + get { + return ResourceManager.GetString("InteractiveAuthNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid Host {0}. /// diff --git a/src/Authentication/Authentication/Properties/Resources.resx b/src/Authentication/Authentication/Properties/Resources.resx index 4e151fe31e8..f9618810abb 100644 --- a/src/Authentication/Authentication/Properties/Resources.resx +++ b/src/Authentication/Authentication/Properties/Resources.resx @@ -223,4 +223,10 @@ Request returned Non-Json response of {0}, Please specify '-OutputFilePath' - \ No newline at end of file + + Interactive authentication is not supported in this session, please run cmdlet 'Connect-MgGraph -UseDeviceAuthentication'. + + + Interactive authentication is not supported in this session, falling back to DeviceCode. Future versions will not automatically fallback to DeviceCode. + + diff --git a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs index 82fe833d2dc..4cb7f5fe4b1 100644 --- a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs +++ b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs @@ -13,7 +13,7 @@ public static class DependencyAssemblyResolver // Catalog our dependencies here to ensure we don't load anything else. private static IReadOnlyDictionary Dependencies = new Dictionary { - { "Microsoft.Identity.Client", new Version("4.23.0.0") }, + { "Microsoft.Identity.Client", new Version("4.29.0.0") }, { "Microsoft.Graph.Auth", new Version("1.0.0.0") }, { "Microsoft.IdentityModel.Tokens", new Version("5.6.0.61018") }, { "Microsoft.IdentityModel.Logging", new Version("5.6.0.61018") }, diff --git a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 index 76845efcd07..dde084b74d3 100644 --- a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 +++ b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 @@ -6,11 +6,11 @@ BeforeAll { } Describe 'Connect-MgGraph In Delegated Mode' { It 'ShouldThrowExceptionWhenInvalidTenantIdIsSpecified' { - { Connect-MgGraph -TenantId "thisdomaindoesnotexist.com" -Scopes 'User.Read.All' -ErrorAction Stop } | Should -Throw -ExpectedMessage "*Tenant 'thisdomaindoesnotexist.com' not found*" + { Connect-MgGraph -TenantId "thisdomaindoesnotexist.com" -Scopes 'User.Read.All' -ErrorAction Stop -UseDeviceAuthentication } | Should -Throw -ExpectedMessage "*Tenant 'thisdomaindoesnotexist.com' not found*" } It 'ShouldThrowExceptionWhenInvalidScopeIsSpecified' { - { Connect-MgGraph -Scopes 'User.Read.XYZ' -ErrorAction Stop } | Should -Throw -ExpectedMessage "*The scope 'User.Read.XYZ offline_access profile openid' does not exist*" + { Connect-MgGraph -Scopes 'User.Read.XYZ' -ErrorAction Stop -UseDeviceAuthentication } | Should -Throw -ExpectedMessage "*The scope 'User.Read.XYZ offline_access profile openid' does not exist*" } } @@ -24,13 +24,13 @@ Describe 'Connect-MgGraph In App Mode' { } } -Describe 'Connect-MgGraph Depencency Resolution' { +Describe 'Connect-MgGraph Dependency Resolution' { BeforeAll { - Install-Module Az.Accounts -Repository PSGallery -Force + Install-Module Az.Accounts -Repository PSGallery -Force -AllowClobber } It 'ShouldLoadMgModuleSideBySideWithAzModule.' { { Connect-AzAccount -ApplicationId $RandomClientId -CertificateThumbprint "Invalid" -Tenant "Invalid" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*Could not find tenant id*" - { Connect-MgGraph -Scopes "inavid.scope" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*AADSTS70011:*" + { Connect-MgGraph -Scopes "inavid.scope" -ErrorAction Stop -UseDeviceAuthentication } | Should -Throw -ExpectedMessage "*AADSTS70011:*" } } \ No newline at end of file