microsoft-authentication-library-for-dotnet icon indicating copy to clipboard operation
microsoft-authentication-library-for-dotnet copied to clipboard

[Bug] AcquireTokenByUsernamePassword returns Object reference not set to an instance of an object when .Net component called from VB6 app

Open CraigW-CIPrecision opened this issue 3 years ago • 5 comments

Logs and network traces Not OK! Start PublicClientApplicationBuilder.Create ok https://login.microsoftonline.com/tiofarma.onmicrosoft.com/ calling AcquireTokenByUsernamePassword - There was an error parsing WS-Trust response from the endpoint. This may occur if there is an issue with your ADFS configuration. See https://aka.ms/msal-net-iwa-troubleshooting for more details. Error Message: Object reference not set to an instance of an object. - at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<GetWsTrustResponseAsync>d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<PerformWsTrustMexExchangeAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<FetchAssertionFromWsTrustAsync>d__7.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<GetTokenResponseAsync>d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<ExecuteAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.<ExecuteAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at DMS6_AzureAuth.AzureAuth.VB$StateMachine_13_LoginTaskWithPwd.MoveNext() - Object reference not set to an instance of an object. - at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<GetWsTrustResponseAsync>d__6.MoveNext()

Which version of MSAL.NET are you using? MSAL.NET 4.46

Platform .NET 4.6.1, VB6

What authentication flow has the issue?

  • Desktop / Mobile
    • [ ] Interactive
    • [ ] Integrated Windows Authentication
    • [x] Username Password
    • [ ] Device code flow (browserless)
  • Web app
    • [ ] Authorization code
    • [ ] On-Behalf-Of
  • Daemon app
    • [ ] Service to Service calls

Other?

Is this a new or existing app? This is an existing app that was working up to a few weeks ago.

Repro THE MSAL library is being used in a VB.NET class library which is COM visible. This is being called from a VB6 application and also a VB.NET test app. When Authenticate is called from the VB6 app the Object error is seen and when called from the VB.net test app it works.

Imports System.Collections.Generic
Imports System.Threading.Tasks
Imports Microsoft.Identity.Client
Imports System.Configuration
Imports DMS6_AzureAuth

Public Interface iAzureAuth_Interface

  ReadOnly Property ClientID As String
  ReadOnly Property Authority As String

  Property ConfigFolder As String 'this is set by calling application so that DLL knows where to look for config file

  Function Authenticate(UserUPN As String, UserPwd As String, ByRef ErrorMessage As String) As Boolean

End Interface


Public Class AzureAuth
  Implements iAzureAuth_Interface

  Friend client_id As String = Nothing
  Friend authority As String = Nothing
  Friend config_path As String = Nothing

  Public ReadOnly Property ClientID As String Implements iAzureAuth_Interface.ClientID
    Get
      Return client_id
    End Get
  End Property

  Private ReadOnly Property iAzureAuth_Interface_Authority As String Implements iAzureAuth_Interface.Authority
    Get
      Return authority
    End Get
  End Property


  Public Property ConfigFolder As String Implements iAzureAuth_Interface.ConfigFolder
    Get
      Return config_path
    End Get
    Set(value As String)
      config_path = value
      value += "\DMS6_AzureAuth.DLL" '& System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase

      Try
        'reset before loading config
        client_id = String.Empty
        authority = String.Empty
        Dim config As Configuration = ConfigurationManager.OpenExeConfiguration(value)
        If Not config.HasFile Then
          Throw New Exception(String.Format("Configuration file missing. Check DMS6_AzureAuth.DLL.config in the following path {0}", config_path))
        End If

        Dim settings = config.AppSettings
        Try
          client_id = settings.Settings("client_id").Value
          authority = settings.Settings("authority").Value

        Catch ex As Exception
          Dim err As String = String.Format("Invalid config file {0}. Error: {1}", value, ex.Message)
          Throw New Exception(err)
        End Try

      Catch ex As Exception
        'config not found or contains missing settings values
        Throw New Exception(ex.Message)

      End Try

    End Set
  End Property


  Private Class AuthParams
    Public UserName As String
    Public Password As String
    Public ErrorMessage As String

  End Class


  Public Function Authenticate(UserUPN As String, UserPwd As String, ByRef ErrorInfo As String) As Boolean Implements iAzureAuth_Interface.Authenticate

    Dim taskParams = New AuthParams With {.UserName = UserUPN, .Password = UserPwd, .ErrorMessage = String.Empty}

    Dim task As Task(Of Boolean) = LoginTaskWithPwd(taskParams)

    If task.Result() Then
      'Console.WriteLine("Authorized!")
      ErrorInfo = taskParams.ErrorMessage

      Return True

    Else
      ErrorInfo = taskParams.ErrorMessage
      'Console.WriteLine("Not Authorized")
      Return False
    End If

  End Function

  Private Async Function LoginTaskWithPwd(params As AuthParams) As Task(Of Boolean)

    Dim publicClientApp As IPublicClientApplication


    Dim authResult As AuthenticationResult
    Dim username As String
    Dim password As New Security.SecureString()
    Dim scopes As New List(Of String)
    Dim accessToken As String = String.Empty

    Try

      'check if clientid and authority have been set first
      If String.IsNullOrEmpty(client_id) OrElse String.IsNullOrEmpty(authority) Then
        Dim err As String = String.Format("Invalid configuration file. Check DMS6_AzureAuth.DLL.config in the following path {0}.", config_path)
        Throw New Exception(err)
      End If
      If String.IsNullOrEmpty(params.Password) Then
        Dim err As String = "Blank password not allowed"
        Throw New Exception(err)
      End If

      params.ErrorMessage = "Start"
      'NB this could error if the client_id is invalid 
      publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithAuthority(authority).Build()
      params.ErrorMessage += " PublicClientApplicationBuilder.Create ok "
      params.ErrorMessage += publicClientApp.Authority

      username = params.UserName
      For Each ch In params.Password
        password.AppendChar(ch)
      Next

      scopes.Add("user.read")
      params.ErrorMessage += " calling AcquireTokenByUsernamePassword "
      authResult = Await publicClientApp.AcquireTokenByUsernamePassword(scopes, username, password).ExecuteAsync()
      params.ErrorMessage += " AcquireTokenByUsernamePassword ok "
      accessToken = authResult.AccessToken
      params.ErrorMessage += " accessToken ok"


      '    Catch ex As MsalUiRequiredException When (ex.Message.Contains("AADSTS65001"))
      ' // Here are the kind of error messages you could have, And possible mitigations

      ' // ------------------------------------------------------------------------
      ' // MsalUiRequiredException: AADSTS65001 : The user Or administrator has Not consented to use the application
      ' // with ID '{appId}' named '{appName}'. Send an interactive authorization request for this user and resource.

      ' // Mitigation you need to get user consent first. This can be done either statically (through the portal),
      ' /// Or dynamically (but this requires an interaction with Azure AD, which Is Not possible with
      ' // the username/password flow)
      ' // Statically: in the portal by doing the following in the "API permissions" tab of the application registration:
      ' // 1. Click "Add a permission" And add all the delegated permissions corresponding to the scopes you want (for instance
      ' // User.Read And User.ReadBasic.All)
      ' // 2. Click "Grant/revoke admin consent for <tenant>") And click "yes".
      ' // Dynamically, if you are Not using .NET Core (which does Not have any Web UI) by
      ' // calling (once only) AcquireTokenInteractive.
      ' // remember that Username/password Is for public client applications that Is desktop/mobile applications.
      ' // If you are using .NET core Or don't want to call AcquireTokenInteractive, you might want to:
      ' // - use device code flow (See https://aka.ms/msal-net-device-code-flow)
      ' // - Or suggest the user to navigate to a URL to consent: https : //login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
      ' // ------------------------------------------------------------------------


    Catch ex As MsalUiRequiredException When (ex.Message.Contains("AADSTS50079")) 'ALSO AADSTS50076 (as seen at TIO

      ' // ------------------------------------------------------------------------
      ' // ErrorCode: invalid_grant
      ' // SubError: basic_action
      ' // MsalUiRequiredException: AADSTS50079 : The user Is required to use multi-factor authentication.
      ' // The tenant admin for your organization has chosen to oblige users to perform multi-factor authentication.
      ' // Mitigation: none for this flow
      ' // Your application cannot use the Username/Password grant.
      ' // Like in the previous case, you might want to use an interactive flow (AcquireTokenInteractive()),
      ' // Or Device Code Flow instead.
      ' // Note this Is one of the reason why using username/password Is Not recommended;
      ' // ------------------------------------------------------------------------
      'Console.WriteLine("The user Is required to use multi-factor authentication.{0}", ex.Message)

      'For DMS assume that if we error at this point that the password was accepted
      params.ErrorMessage = ex.Message
      accessToken = "MFA request"

    Catch ex As MsalUiRequiredException When (ex.Message.Contains("AADSTS50076"))
      'For DMS assume that if we error at this point that the password was accepted
      params.ErrorMessage = ex.Message
      accessToken = "MFA request"


      'Catch ex As MsalUiRequiredException When (ex.Message.Contains("AADSTS70002"))

      '  ' // ------------------------------------------------------------------------
      '  ' // ex.ErrorCode: invalid_grant
      '  ' // subError: null
      '  ' // Message = "AADSTS70002: Error validating credentials.
      '  ' // AADSTS50126: Invalid username or password
      '  ' // In the case of a managed user (user from an Azure AD tenant opposed to a
      '  ' // federated user, which would be owned
      '  ' // in another IdP through ADFS), the user has entered the wrong password
      '  ' // Mitigation: ask the user to re-enter the password
      '  ' // ------------------------------------------------------------------------
      '  'Console.WriteLine("The user has entered the wrong password.{0}", ex.Message)
      '  Throw New Exception(ex.Message, ex)


      'Catch ex As MsalUiRequiredException When (ex.Message.Contains("ADSTS50034"))
      '  ' // ------------------------------------------------------------------------
      '  ' // ex.ErrorCode: invalid_grant
      '  ' // subError: null
      '  ' // MsalServiceException: ADSTS50034: To sign into this application the account must be added to
      '  ' // the {domainName} directory.
      '  ' // or The user account does not exist in the {domainName} directory. To sign into this application,
      '  ' // the account must be added to the directory.
      '  ' // The user was not found in the directory
      '  ' // Explanation: wrong username
      '  ' // Mitigation: ask the user to re-enter the username.
      '  ' // ------------------------------------------------------------------------

      '  'Console.WriteLine("The user was not found in the directory.{0}", ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalServiceException When (ex.ErrorCode = "invalid_request")

      '  ' // ------------------------------------------------------------------------
      '  ' // AADSTS90010: The grant type is not supported over the /common or /consumers endpoints.
      '  ' // Please use the /organizations or tenant-specific endpoint.
      '  ' // you used common.
      '  ' // Mitigation: as explained in the message from Azure AD, the authority you use in the application needs
      '  ' // to be tenanted or otherwise "organizations". change the
      '  ' // "Tenant": property in the appsettings.json to be a GUID (tenant Id), or domain name (contoso.com)
      '  ' // if such a domain is registered with your tenant
      '  ' // or "organizations", if you want this application to sign-in users in any Work and School accounts.
      '  ' // ------------------------------------------------------------------------
      '  'Console.WriteLine(ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalServiceException When (ex.ErrorCode = "unauthorized_client")
      '  ' // ------------------------------------------------------------------------
      '  ' // AADSTS700016: Application with identifier '{clientId}' was not found in the directory '{domain}'.
      '  ' // This can happen if the application has not been installed by the administrator of the tenant or consented
      '  ' // to by any user in the tenant.
      '  ' // You may have sent your authentication request to the wrong tenant
      '  ' // Cause: The clientId in the appsettings.json might be wrong
      '  ' // Mitigation: check the clientId and the app registration
      '  ' // ------------------------------------------------------------------------
      '  'Console.WriteLine(ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalServiceException When (ex.ErrorCode = "invalid_client")
      '  ' // ------------------------------------------------------------------------
      '  ' // AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
      '  ' // Explanation: this can happen if your application was not registered as a public client application in Azure AD
      '  ' // Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`
      '  ' // ------------------------------------------------------------------------

      '  'Console.WriteLine("Invalid client. this can happen if your application was not registered as a public client application in Azure AD Mitigation:  in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`{0}", ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalServiceException
      '  'Console.WriteLine("MsalServiceException{0}{1}", ex.ErrorCode, ex.Message)
      '  Throw New Exception(ex.Message, ex)


      'Catch ex As MsalClientException When (ex.ErrorCode = "unknown_user_type")
      '  '{
      '  ' // Message = "Unsupported User Type 'Unknown'. Please see https://aka.ms/msal-net-up"
      '  ' // The user Is Not recognized as a managed user, Or a federated user. Azure AD was Not
      '  ' // able to identify the IdP that needs to process the user
      '  ' Throw New ArgumentException("U/P: Wrong username", ex);
      '  '}
      '  'Throw New ArgumentException("U/P: Wrong username", ex)
      '  'Console.WriteLine("U/P: Wrong username{0}", ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalClientException When (ex.ErrorCode = "user_realm_discovery_failed")
      '  '{
      '  ' // The user Is Not recognized as a managed user, Or a federated user. Azure AD was Not
      '  ' // able to identify the IdP that needs to process the user. That's for instance the case
      '  ' // if you use a phone number
      '  ' Throw New ArgumentException("U/P: Wrong username", ex);
      '  '}
      '  'Console.WriteLine("U/P: Wrong username{0}", ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalClientException When (ex.ErrorCode = "unknown_user")
      '  '{
      '  ' // the username was probably empty
      '  ' // ex.Message = "Could not identify the user logged into the OS. See https://aka.ms/msal-net-iwa for details."
      '  ' Throw New ArgumentException("U/P: Wrong username", ex);
      '  '}
      '  'Console.WriteLine("U/P: Wrong username{0}{1}", ex.ErrorCode, ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalClientException When (ex.ErrorCode = "parsing_wstrust_response_failed")
      '  '{
      '  ' // ------------------------------------------------------------------------
      '  ' // In the case of a Federated user (that Is owned by a federated IdP, as opposed to a managed user owned in an Azure AD tenant)
      '  ' // ID3242: The security token could Not be authenticated Or authorized.
      '  ' // The user does Not exist Or has entered the wrong password
      '  ' // ------------------------------------------------------------------------
      '  '}
      '  'Console.WriteLine("The user does Not exist Or has entered the wrong password{0}{1}", ex.ErrorCode,      ex.Message)
      '  Throw New Exception(ex.Message, ex)

      'Catch ex As MsalClientException
      '  'Console.WriteLine("MsalClientException{0}{1}", ex.ErrorCode, ex.Message)
      '  Throw New Exception(ex.Message, ex)

    Catch ex As Exception
      'Console.WriteLine("Unknown error{0}{1}", ex.Message)
      'Throw New Exception(ex.Message, ex)
      Dim msgs As New List(Of String)
      'Dim fullerror As String = ex.ToString()
      msgs.Add(params.ErrorMessage)



      Do
        msgs.Add(ex.Message)
        msgs.Add(ex.StackTrace.ToString())
        ex = ex.InnerException
      Loop While Not (ex Is Nothing)
      params.ErrorMessage = String.Join(" - ", msgs)
      'params.ErrorMessage += String.Join(" ## ", fullerror)

    End Try

    Return accessToken <> String.Empty


  End Function


End Class

Module DMS6_AzureAuth_Test

  Sub Main()
    Dim username As String
    Dim password As String = String.Empty
    Dim cki As ConsoleKeyInfo
    Dim azureAuth As DMS6_AzureAuth.iAzureAuth_Interface
    Dim errmsg As String = String.Empty

    Try
      azureAuth = New DMS6_AzureAuth.AzureAuth
      'set config path
      azureAuth.ConfigFolder = Environment.CurrentDirectory


    Catch ex As Exception
      Console.ForegroundColor = ConsoleColor.Red
      Console.WriteLine("Exception raised:")
      Console.WriteLine(ex.Message)

    End Try

    Console.ForegroundColor = ConsoleColor.Yellow
    Console.Write("Client ID:")
    Console.ResetColor()
    Console.WriteLine(azureAuth.ClientID)
    Console.ForegroundColor = ConsoleColor.Yellow
    Console.Write("Authority:")
    Console.ResetColor()
    Console.WriteLine(azureAuth.Authority)

    Do
      password = String.Empty
      Console.ResetColor()
      Console.Write("Enter username:")
      Console.ForegroundColor = ConsoleColor.Magenta
      username = Console.ReadLine()
      Console.ResetColor()
      Console.Write("Enter password:")
      Console.ForegroundColor = ConsoleColor.Magenta
      Do
        cki = Console.ReadKey(True)
        If cki.Key <> ConsoleKey.Enter Then
          Console.Write("*")
          password += cki.KeyChar
        End If
      Loop While cki.Key <> ConsoleKey.Enter
      Console.WriteLine()
      Console.ForegroundColor = ConsoleColor.Yellow
      Console.WriteLine("Authorising...")
      Try
        If azureAuth.Authenticate(username, password, errmsg) Then
          Console.ForegroundColor = ConsoleColor.Green
          Console.WriteLine("Authorized!")
          Console.WriteLine(errmsg)

        Else
          Console.ForegroundColor = ConsoleColor.Red
          Console.WriteLine("Not Authorized!")
          Console.WriteLine(errmsg)
        End If

      Catch ex As Exception
        Console.ForegroundColor = ConsoleColor.Red
        Console.WriteLine("Exception raised:")
        Console.WriteLine(ex.Message)
        Console.WriteLine(ex.InnerException.Message)
      End Try

      Console.ResetColor()
      Console.WriteLine("Press a ESC to end, otherwise retry")
      cki = Console.ReadKey(True)
      If cki.Key = ConsoleKey.Escape Then Exit Do
      If cki.Key = ConsoleKey.R Then
        're-read config
        Try
          azureAuth.ConfigFolder = Environment.CurrentDirectory
          Console.ForegroundColor = ConsoleColor.Yellow
          Console.Write("Client ID:")
          Console.ResetColor()
          Console.WriteLine(azureAuth.ClientID)
          Console.ForegroundColor = ConsoleColor.Yellow
          Console.Write("Authority:")
          Console.ResetColor()
          Console.WriteLine(azureAuth.Authority)
          Console.ResetColor()

        Catch ex As Exception
          Console.ForegroundColor = ConsoleColor.Red
          Console.WriteLine("Exception raised:")
          Console.WriteLine(ex.Message)

        End Try

      End If

    Loop

  End Sub

End Module
Option Explicit

Private moAzureAuth As iAzureAuth_Interface 'CI/DMS6/CRN1034


Private Sub cmdLogon_Click()
  Dim strErrorDesc As String
  
  On Error GoTo ErrTrap
  txtResult.Text = "..."
  Me.Refresh
  If moAzureAuth Is Nothing Then
    Set moAzureAuth = New DMS6_AzureAuth.AzureAuth
    moAzureAuth.ConfigFolder = App.Path 'this could error if config not found or if data missing
  End If
  
  If moAzureAuth.Authenticate(Trim$(txtUserID), txtPwd, strErrorDesc) Then
    txtResult.Text = "OK" * vbCrLf & strErrorDesc
  Else
    txtResult.Text = "Not OK!" & vbCrLf & strErrorDesc
  End If
  Exit Sub
  
ErrTrap:
  txtResult.Text = Err.Description

End Sub

Expected behavior The VB6 app authenticates correctly.

Actual behavior The VB6 app gives an object error When the same class library is called from a VB.NET test client AcquireTokenByUsernamePassword returns without error.

Authorising...
Authorized!
Start PublicClientApplicationBuilder.Create ok https://login.microsoftonline.com
/tiofarma.onmicrosoft.com/ calling AcquireTokenByUsernamePassword  AcquireTokenB
yUsernamePassword ok  accessToken ok
Press a ESC to end, otherwise retry

Possible solution

Additional context / logs / screenshots / links to code

Add any other context about the problem here, such as logs and screenshots, and even links to code.

CraigW-CIPrecision avatar Aug 18 '22 16:08 CraigW-CIPrecision

Can you try to use WAM instead? It will do the ROPC flow instead of MSAL, and it's a Windows component. See https://aka.ms/msal-net-wam

bgavrilMS avatar Aug 22 '22 13:08 bgavrilMS

Thanks Bogdan,

Are there any examples of using ROPC with WAM?

Thanks Craig

Craig Woods Technical Lead

t. +44 (0)1722 424100 e. @.*** www.ciprecision.comCIPrecision

​Any opinions expressed in this email are those of the individual author and do not necessarily reflect those of the Company. The Company does not take any responsibility for the views of the author. This email and any files transmitted with it are confidential and solely for the use of the intended recipient. If you are not the intended recipient or the person responsible for delivering to the intended recipient, be advised that you have received this email in error and that any use is strictly prohibited. If you have received this email in error, advise the sender immediately by using the reply facility in your email software. We have taken every reasonable precaution to ensure that any attachments to this email are swept for viruses. However, we cannot accept liability for any damage sustained as a result of software viruses and advise you to carry out your own virus checks before opening any attachments. CI Electronics Limited, trading as CI Precision, registered in England and Wales, company number 00850159 CI Systems Limited, trading as CI Precision, registered in England and Wales, company number 00929573 2 Brunel Road, Churchfields, Salisbury, Wiltshire SP2 7PX

From: Bogdan Gavril @.> Sent: 22 August 2022 14:40 To: AzureAD/microsoft-authentication-library-for-dotnet @.> Cc: Craig Woods @.>; Author @.> Subject: Re: [AzureAD/microsoft-authentication-library-for-dotnet] [Bug] AcquireTokenByUsernamePassword returns Object reference not set to an instance of an object when .Net component called from VB6 app (Issue #3614)

CAUTION: This email originated from outside your organization. Exercise caution when opening attachments or clicking links, especially from unknown senders.

Can you try to use WAM instead? It will do the ROPC flow instead of MSAL, and it's a Windows component. See https://aka.ms/msal-net-wam

— Reply to this email directly, view it on GitHubhttps://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3614#issuecomment-1222378347, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AH2Q7YQSGRDYW3YB57O62U3V2N7J3ANCNFSM565XE5BQ. You are receiving this because you authored the thread.Message ID: @.@.>>

CraigW-CIPrecision avatar Aug 24 '22 09:08 CraigW-CIPrecision

Just enable WAM as described here on the PublicClientApplication object - https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/WAM#to-enable-wam-preview

Then call AcquireTokenByUsernamePassword. WAM will take over.

bgavrilMS avatar Aug 24 '22 10:08 bgavrilMS

Thanks Bogdan,

I will give that a try.

Best regards Craig

Craig Woods Technical Lead

t. +44 (0)1722 424100 e. @.*** www.ciprecision.comCIPrecision

​Any opinions expressed in this email are those of the individual author and do not necessarily reflect those of the Company. The Company does not take any responsibility for the views of the author. This email and any files transmitted with it are confidential and solely for the use of the intended recipient. If you are not the intended recipient or the person responsible for delivering to the intended recipient, be advised that you have received this email in error and that any use is strictly prohibited. If you have received this email in error, advise the sender immediately by using the reply facility in your email software. We have taken every reasonable precaution to ensure that any attachments to this email are swept for viruses. However, we cannot accept liability for any damage sustained as a result of software viruses and advise you to carry out your own virus checks before opening any attachments. CI Electronics Limited, trading as CI Precision, registered in England and Wales, company number 00850159 CI Systems Limited, trading as CI Precision, registered in England and Wales, company number 00929573 2 Brunel Road, Churchfields, Salisbury, Wiltshire SP2 7PX

From: Bogdan Gavril @.> Sent: 24 August 2022 11:47 To: AzureAD/microsoft-authentication-library-for-dotnet @.> Cc: Craig Woods @.>; Author @.> Subject: Re: [AzureAD/microsoft-authentication-library-for-dotnet] [Bug] AcquireTokenByUsernamePassword returns Object reference not set to an instance of an object when .Net component called from VB6 app (Issue #3614)

CAUTION: This email originated from outside your organization. Exercise caution when opening attachments or clicking links, especially from unknown senders.

Just enable WAM as described here on the PublicClientApplication object - https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/WAM#to-enable-wam-preview

Then call AcquireTokenByUsernamePassword. WAM will take over.

— Reply to this email directly, view it on GitHubhttps://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3614#issuecomment-1225550004, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AH2Q7YSQNYZMYKO4GE7F53TV2X4SZANCNFSM565XE5BQ. You are receiving this because you authored the thread.Message ID: @.@.>>

CraigW-CIPrecision avatar Aug 24 '22 11:08 CraigW-CIPrecision

I have tried using WithBrokerPreview but I get the same results. My vb.net test client works correctly but when I access the same .net library using COM I see the same object error:

Not OK! Start PublicClientApplicationBuilder.Create (with broker preview) ok https://login.microsoftonline.com/tiofarma.onmicrosoft.com/ calling AcquireTokenByUsernamePassword - There was an error parsing WS-Trust response from the endpoint. This may occur if there is an issue with your ADFS configuration. See https://aka.ms/msal-net-iwa-troubleshooting for more details. Error Message: Object reference not set to an instance of an object. - at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<GetWsTrustResponseAsync>d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<PerformWsTrustMexExchangeAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<FetchAssertionFromWsTrustAsync>d__7.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<GetTokenResponseAsync>d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<ExecuteAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.<ExecuteAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at DMS6_AzureAuth.AzureAuth.VB$StateMachine_13_LoginTaskWithPwd.MoveNext() - Object reference not set to an instance of an object. - at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<GetWsTrustResponseAsync>d__6.MoveNext()

CraigW-CIPrecision avatar Sep 06 '22 16:09 CraigW-CIPrecision

I have also encountered this issue in a C# application. It doesn't matter which library I use, the end result is always the same:

There was an error parsing WS-Trust response from the endpoint. This may occur if there is an issue with your ADFS configuration. See https://aka.ms/msal-net-iwa-troubleshooting for more details. Error Message: Object reference not set to an instance of an object.

at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<GetWsTrustResponseAsync>d__6.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.WsTrust.CommonNonInteractiveHandler.<PerformWsTrustMexExchangeAsync>d__5.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<FetchAssertionFromWsTrustAsync>d__7.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<GetTokenResponseAsync>d__6.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.Internal.Requests.UsernamePasswordRequest.<ExecuteAsync>d__5.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__12.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.<ExecuteAsync>d__5.MoveNext()

hex-chris avatar Oct 18 '22 12:10 hex-chris

If you write a C# app and try with the same user, do you get the same error or does it work? Can we get logs please?

bgavrilMS avatar Oct 18 '22 15:10 bgavrilMS

Yes, this fails within a new C# app for any user. We have managed to trace down the issue though. Using packet capture we noticed the ADFS server is returning the error "MSIS7068: Access denied.".

It appears that when the Microsoft.Identity.Client.WsTrust library recieves back a SOAP from the ADFS server it doesn't handle it gracefully, and simply throws a null ref exception.

hex-chris avatar Oct 18 '22 15:10 hex-chris

Is it possible to copy paste the request and response that results in "Access Denied" ? So that we can fix MSAL.

bgavrilMS avatar Oct 18 '22 16:10 bgavrilMS

C# snippet:

var clientAuth = PublicClientApplicationBuilder("xxxx").WithTenantId(xxxx).Build(); var token = await clientAuth.AcquireTokenByUsernamePassword(scopes, "user", "pass").ExecuteAsync();

The request being generated and sent to the ADFS server is as follows:

<s:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:s="http://www.w3.org/2003/05/soap-envelope"> <s:Header> <wsa:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action> wsa:MessageIDurn:uuid:d73362f9-aaaa-4be1-8416-ca380baaaaa</wsa:MessageID> wsa:ReplyTo wsa:Addresshttp://www.w3.org/2005/08/addressing/anonymous</wsa:Address> </wsa:ReplyTo> <wsa:To s:mustUnderstand="1">https://xxxxxx.com/adfs/services/trust/2005/usernamemixed</wsa:To> <wsse:Security s:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsu:Timestamp wsu:Id="MSATimeStamp"> wsu:Created2022-10-18T13:55:05.068Z</wsu:Created> wsu:Expires2022-10-18T14:05:05.068Z</wsu:Expires> </wsu:Timestamp> <wsse:UsernameToken wsu:Id="UnPwSecTok2005-ca81f206-4d53-bbbb-aaaa-6a8a30993111"> wsse:Usernamexxxxxx</wsse:Username> wsse:Passwordxxxxxx</wsse:Password> </wsse:UsernameToken> </wsse:Security> </s:Header> <s:Body> <wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust"> <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> wsa:EndpointReference wsa:Addressurn:federation:MicrosoftOnline</wsa:Address> </wsa:EndpointReference> </wsp:AppliesTo> wst:KeyTypehttp://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType> wst:RequestTypehttp://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType> </wst:RequestSecurityToken> </s:Body> </s:Envelope>

The response from the ADFS server is:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action> <a:RelatesTo>urn:uuid:d73362f9-aaaa-4be1-8416-ca380baaaaa</a:RelatesTo> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <u:Timestamp u:Id="_0"> <u:Created>2022-10-18T13:54:59.751Z</u:Created> <u:Expires>2022-10-18T13:59:59.751Z</u:Expires> </u:Timestamp> </o:Security> </s:Header> <s:Body> <s:Fault> <s:Code> <s:Value>s:Sender</s:Value> <s:Subcode> <s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/02/trust">a:FailedAuthentication</s:Value> </s:Subcode> </s:Code> <s:Reason> <s:Text xml:lang="en-US">MSIS7068: Access denied.</s:Text> </s:Reason> <s:Detail> <IssuanceAuthorizationFault xmlns="http://schemas.microsoft.com/ws/2009/12/identityserver/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/> </s:Detail> </s:Fault> </s:Body> </s:Envelope>

hex-chris avatar Oct 19 '22 13:10 hex-chris