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