Hugoberry / dax-interactive

6 stars 0 forks source link

Error if no User ID / Password specified #1

Open marcosqlbi opened 1 year ago

marcosqlbi commented 1 year ago

I get this error if I don't specify username/password - should I get a login window instead?

Error: System.ArgumentException: Authentication failed: User ID and Password are required when user interface is not available.

Hugoberry commented 1 year ago

The login window was available in v0.0.19 when the extension was using OleDb connection. For greater compatibility I've moved the using Adomd and I'm still investigating how to trigger the UI for OAuth authentication flow.

marcosqlbi commented 1 year ago

Take a look at Bravo source code - we had a similar issue and probably you cannot use the same technique, but it can probably help. When you parse the connection string, if it doesn't contain "Password" then use MSAL to get the OAUTH token to include in the connection string.

To get the OAUTH token via MSAL you can use this class: https://github.com/sql-bi/Bravo/blob/main/src/Infrastructure/Helpers/MsalHelper.cs

Thanks to @albertospelta for providing the info I reported here!

Hugoberry commented 1 year ago

Thanks for pointing this out @marcosqlbi. That's very helpful.

I was usually looking for inspiration on how to Adomd from DAX Studio, and never checked the internals for Bravo.

Will have to check if using MSAL is the right approach as there might be some limitations to the dotnet interactive API as the SQL extension suffers from a similar issue.

Hugoberry commented 1 year ago

Adding the stack for future reference:


Error: System.ArgumentException: Authentication failed: User ID and Password are required when user interface is not available.
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.HandlePaaSInfrastructureConnection()
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.ValidateAndCompleteConnectionConfiguration()
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.SetConnectionString(String cs)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo..ctor(String connectionString)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Microsoft.AnalysisServices.AdomdClient.AdomdConnection.IXmlaClientProviderEx.set_ConnectionString(String value)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.set_ConnectionString(String value)
   at Dax.Interactive.DaxKernel.HandleAsync(SubmitCode submitCode, KernelInvocationContext context)
   at Microsoft.DotNet.Interactive.Kernel.HandleAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\Kernel.cs:line 298
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<BuildPipeline>b__6_0(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation _) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 59
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_1.<<BuildPipeline>b__3>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 75
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.CompositeKernel.LoadExtensions(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation next) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\CompositeKernel.cs:line 133
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_0.<<BuildPipeline>g__Combine|2>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 76
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.SendAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 50
albertospelta commented 1 year ago

Definitely not a solution, but as a temporary workaround this should allow to use the login window. Create a new Microsoft.DotNet.Interactive.App.appsettings.json file in the %USERPROFILE%\.nuget\packages\microsoft.dotnet-interactive\<VERSION>\tools\net6.0\any folder

image

{
  "asConfiguration": {
    "runtime": {
      "isProcessWithUI": true
    }
  }
} 

This allows ADOMD to trigger the MSAL OAuth login even if the host process is recognized as to be non-interactive (it does not have any window/console handle)

Hugoberry commented 1 year ago

Worth mentioning that the workaround with appsettings above doesn't work on Linux. Here is the patch for Dockerfile

ENV DOTNET_INTERACTIVE_VERSION 1.0.357501
# Install lastest build of Microsoft.DotNet.Interactive
RUN dotnet tool install -g Microsoft.dotnet-interactive --version $DOTNET_INTERACTIVE_VERSION --add-source "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json"

RUN echo "{\
  \"asConfiguration\": {\
    \"runtime\": {\
      \"isProcessWithUI\": true\
    }\
  }\
}\
" > ${HOME}/.dotnet/tools/.store/microsoft.dotnet-interactive/${DOTNET_INTERACTIVE_VERSION}/microsoft.dotnet-interactive/${DOTNET_INTERACTIVE_VERSION}/tools/net7.0/any/Microsoft.DotNet.Interactive.App.appsettings.json

results in

System.InvalidOperationException: A device-code authentication flow is needed and a notification callback was not provided!
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.DeviceCodeAuthenticationHandle.AcquireTokenImpl(IPublicClientApplication app, IEnumerable`1 scopes, String userId, IAccount account, Action`1 onDeviceCodeNotification)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.DeviceCodeAuthenticationHandle.AcquireToken(IPublicClientApplication app, IEnumerable`1 scopes, String userId, Action`1 onDeviceCodeNotification)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.MsalAuthenticationService.AuthenticateUser(AuthenticationOptions options, AuthenticationInformation authInfo)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.AuthenticationManager.Authenticate(AuthenticationOptions options, String identityProvider, String resource, String tenantId, String userId, String password, Boolean isForAsAzureRedirection)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.AcquireToken(IConnectivityOwner owner, String resource, String tenantId, Boolean isForAsAzureRedirection)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.ResolveHTTPConnectionPropertiesForPaaSInfrastructure(IConnectivityOwner owner, Uri& dataSourceUri, Boolean acquireAADToken, Boolean returnCloudConnectionAuthenticationProperties, Boolean isRedirectedWorkspace, String tenantId, String& paasCoreServerName, CloudConnectionAuthenticationProperties& cloudConnectionAuthenticationProperties)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.AcquireAADTokenAndResolveHTTPConnectionPropertiesForPaaSInfrastructure(IConnectivityOwner owner, Uri& dataSourceUri)
   at Microsoft.AnalysisServices.AdomdClient.HttpStream.Create(IConnectivityOwner owner, ConnectionInfo info, XmlaDataType desiredRequestType, XmlaDataType desiredResponseType, Int32 readTimeout, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenHttpConnection(ConnectionInfo connectionInfo, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenConnectionAndCheckIfSessionTokenNeeded(ConnectionInfo connectionInfo)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.<>c__DisplayClass99_0.<OpenConnection>b__0()
   at Microsoft.AnalysisServices.Security.TransparentUserContext.ExecuteInUserContextImpl[TResult](Func`1 action)
   at Microsoft.AnalysisServices.Security.UserContext.ExecuteInUserContext[TResult](Func`1 action)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenConnection(ConnectionInfo connectionInfo, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.Connect(ConnectionInfo connectionInfo, Boolean beginSession)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Connect()
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Microsoft.AnalysisServices.AdomdClient.AdomdConnection.IXmlaClientProviderEx.ConnectXmla()
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.ConnectToXMLA(Boolean createSession, Boolean isHTTP)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.Open()
   at Dax.Interactive.DaxKernel.HandleAsync(SubmitCode submitCode, KernelInvocationContext context)
   at Microsoft.DotNet.Interactive.Kernel.HandleAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\Kernel.cs:line 325
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<BuildPipeline>b__6_0(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation _) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 60
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_1.<<BuildPipeline>b__3>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 75
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.CompositeKernel.LoadExtensions(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation next) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\CompositeKernel.cs:line 130
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_0.<<BuildPipeline>g__Combine|2>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 73
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.SendAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 41
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.DeviceCodeAuthenticationHandle.AcquireTokenImpl(IPublicClientApplication app, IEnumerable`1 scopes, String userId, IAccount account, Action`1 onDeviceCodeNotification)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.DeviceCodeAuthenticationHandle.AcquireToken(IPublicClientApplication app, IEnumerable`1 scopes, String userId, Action`1 onDeviceCodeNotification)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.MsalAuthenticationHandle.MsalAuthenticationService.AuthenticateUser(AuthenticationOptions options, AuthenticationInformation authInfo)
   at Microsoft.AnalysisServices.AdomdClient.Authentication.AuthenticationManager.Authenticate(AuthenticationOptions options, String identityProvider, String resource, String tenantId, String userId, String password, Boolean isForAsAzureRedirection)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.AcquireToken(IConnectivityOwner owner, String resource, String tenantId, Boolean isForAsAzureRedirection)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.ResolveHTTPConnectionPropertiesForPaaSInfrastructure(IConnectivityOwner owner, Uri& dataSourceUri, Boolean acquireAADToken, Boolean returnCloudConnectionAuthenticationProperties, Boolean isRedirectedWorkspace, String tenantId, String& paasCoreServerName, CloudConnectionAuthenticationProperties& cloudConnectionAuthenticationProperties)
   at Microsoft.AnalysisServices.AdomdClient.ConnectionInfo.AcquireAADTokenAndResolveHTTPConnectionPropertiesForPaaSInfrastructure(IConnectivityOwner owner, Uri& dataSourceUri)
   at Microsoft.AnalysisServices.AdomdClient.HttpStream.Create(IConnectivityOwner owner, ConnectionInfo info, XmlaDataType desiredRequestType, XmlaDataType desiredResponseType, Int32 readTimeout, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenHttpConnection(ConnectionInfo connectionInfo, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenConnectionAndCheckIfSessionTokenNeeded(ConnectionInfo connectionInfo)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.<>c__DisplayClass99_0.<OpenConnection>b__0()
   at Microsoft.AnalysisServices.Security.TransparentUserContext.ExecuteInUserContextImpl[TResult](Func`1 action)
   at Microsoft.AnalysisServices.Security.UserContext.ExecuteInUserContext[TResult](Func`1 action)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.OpenConnection(ConnectionInfo connectionInfo, Boolean& isSessionTokenNeeded)
   at Microsoft.AnalysisServices.AdomdClient.XmlaClient.Connect(ConnectionInfo connectionInfo, Boolean beginSession)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Connect()
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Microsoft.AnalysisServices.AdomdClient.AdomdConnection.IXmlaClientProviderEx.ConnectXmla()
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.ConnectToXMLA(Boolean createSession, Boolean isHTTP)
   at Microsoft.AnalysisServices.AdomdClient.AdomdConnection.Open()
   at Dax.Interactive.DaxKernel.HandleAsync(SubmitCode submitCode, KernelInvocationContext context)
   at Microsoft.DotNet.Interactive.Kernel.HandleAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\Kernel.cs:line 325
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<BuildPipeline>b__6_0(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation _) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 60
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_1.<<BuildPipeline>b__3>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 75
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.CompositeKernel.LoadExtensions(KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation next) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\CompositeKernel.cs:line 130
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.<>c__DisplayClass6_0.<<BuildPipeline>g__Combine|2>d.MoveNext() in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 73
--- End of stack trace from previous location ---
   at Microsoft.DotNet.Interactive.KernelCommandPipeline.SendAsync(KernelCommand command, KernelInvocationContext context) in D:\a\_work\1\s\src\Microsoft.DotNet.Interactive\KernelCommandPipeline.cs:line 41
Hugoberry commented 1 year ago

Using useEmbeddedBrowser=true path also falls short because of the lack of cross-platform webview support of MSAL.

Error: MSAL.NetCore.4.43.0.0.MsalClientException: 
    ErrorCode: no_system_webview
Microsoft.Identity.Client.MsalClientException: If you have a Windows application which targets net5 or net5-windows, please change the target to net5-windows10.0.17763.0, which provides support from Win7 to Win10. For details, see https://github.com/dotnet/designs/blob/main/accepted/2020/platform-checks/platform-checks.mdIf you have a cross-platform (Windows, Mac, Linux) application which targets net5, please dual target net5 and net5-windows10.0.17763.0.Your installer should deploy the net5 version on Mac and Linux and the net5-window10.0.17763.0 on Win7 - Win10.For details, see https://github.com/dotnet/designs/blob/main/accepted/2020/platform-checks/platform-checks.mdIf you have a .NET Core 3.1 app, please reference the NuGet package Microsoft.Identity.Client.Desktop and call the extension method .WithDesktopFeatures() first.For details, see https://aka.ms/msal-net-webview2 or use the system WebView - see https://aka.ms/msal-net-os-browser
   at Microsoft.Identity.Client.Platforms.Shared.NetStdCore.NetCoreWebUIFactory.CreateAuthenticationDialog(CoreUIParent coreUIParent, WebViewPreference webViewPreference, RequestContext requestContext)
   at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.CreateWebAuthenticationDialog()
   at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.FetchAuthCodeAndPkceVerifierAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.GetTokenResponseAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.ExecuteAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenInteractiveParameters interactiveParameters, CancellationToken cancellationToken)
   at Dax.Interactive.ConnectionHelper.AcquireTokenInteractiveAsync(String userPrincipalName, String claims, CancellationToken cancellationToken)

I'm exploring the option of using the loopback redirect and capturing the auth token.