Skip to content

[Bug]: Certificate-based authentication fails in Codeunit 9357 "Graph Auth. Client Credentials #29558

@AmeyDeshTQ

Description

@AmeyDeshTQ

Describe the issue

In the SetParameters procedure (overload for Certificates), there is a variable assignment error. The code performs Certificate := Certificate; instead of Certificate := NewCertificate;. This results in the certificate payload being empty when passed to the underlying OAuth2 library.

Error Message: This triggers System.ArgumentException: Array may not be empty or null. (Parameter 'rawData') in Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.CertificateFromBase64.

Stack Trace:

Error accessing Website BC270
Type: Microsoft.Dynamics.Nav.Types.Exceptions.NavNCLDotNetInvokeException
ErrorCode: -1
ShouldCalculateALCallStack: True
SuppressMessage: False
ContainsPersonalOrRestrictedInformation: False
DiagnosticsSuppress: True
MessageWithoutPrivateInformation: A call to Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificate failed with this message: Array may not be empty or null. (Parameter 'rawData')
SuppressExceptionCreatedEvent: False
UntrappableError: False
FatalityScope: Call
ErrorLevel: Error
Source: Microsoft.Dynamics.Nav.Client.ServiceConnection
HResult: -2146233088
StackTrace:
at Microsoft.Dynamics.Nav.Client.ConnectionStateManager.CallServer[T](Func1 callServer, Nullable1 options) in s:\repo\src\Platform\Client\Shared\Prod.Client.ServiceConnection\ConnectionStateManager.cs:line 54
at Microsoft.Dynamics.Nav.Client.ServiceConnection.CallServer[T](Func1 callServer, Nullable1 options) in s:\repo\src\Platform\Client\Shared\Prod.Client.ServiceConnection\ServiceConnection.cs:line 0
at Microsoft.Dynamics.Nav.Client.ServerInvocationHandler.CallServerSync[T](Func1 callServer, Nullable1 options) in s:\repo\src\Platform\Client\Shared\Prod.Client.ServiceConnection\ServerInvocationHandler.cs:line 99
at Microsoft.Dynamics.Nav.Client.ServiceConnectionBase.ActionField(NavRecordState& state, NavDataSet recDataSet, Int32 controlId, Int32 controlEventId, Object[] args) in s:\repo\src\Platform\ClientServerShared\Prod.Types\ServiceConnection\ServiceConnectionBase.cs:line 623
at Microsoft.Dynamics.Nav.Client.ServiceConnection.Microsoft.Dynamics.Nav.Client.IService.ActionField(NavRecordState& state, NavDataSet recDataSet, Int32 controlId, Int32 controlEventId, Object[] args)
at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.ActionMethod(Int32 controlId, Int32 controlEventId, Object[] args) in s:\repo\src\Platform\Client\Shared\Prod.Client.UI\Databinder\NSTDataAccess.cs:line 797
at Microsoft.Dynamics.Nav.Client.Actions.InvokePageTriggerAction.InvokeTrigger(LogicalControl logicalControl, UISession uiSession) in s:\repo\src\Platform\Client\Shared\Prod.Client.UI\Actions\FormActions\InvokePageTriggerAction.cs:line 115
at Microsoft.Dynamics.Nav.Client.Actions.InvokePageTriggerAction.InvokeCore(LogicalControl logicalControl, Object state, UISession uiSession) in s:\repo\src\Platform\Client\Shared\Prod.Client.UI\Actions\FormActions\InvokePageTriggerAction.cs:line 76
at Microsoft.Dynamics.Framework.UI.LogicalAction.InvokeCoreWithErrorHandling(LogicalControl logicalControl, Object state, UISession uiSession) in s:\repo\src\Platform\Client\Shared\Prod.ClientFwk\Actions\LogicalAction.cs:line 543

Type: Microsoft.Dynamics.Nav.Types.Exceptions.NavNCLDotNetInvokeException
ErrorCode: -1
ShouldCalculateALCallStack: True
SuppressMessage: False
ContainsPersonalOrRestrictedInformation: False
DiagnosticsSuppress: True
MessageWithoutPrivateInformation: A call to Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificate failed with this message: Array may not be empty or null. (Parameter 'rawData')
SuppressExceptionCreatedEvent: False
UntrappableError: False
FatalityScope: Call
ErrorLevel: Error
Source: Microsoft.Dynamics.Nav.Ncl
HResult: -2146233088
StackTrace:
at Microsoft.Dynamics.Nav.Runtime.NavDotNet.Invoke[T](String methodName, UInt32 methodIndex, BindingFlags flags, ParameterModifier modifier, Type[] referenceTypes, Object[] arguments)
at Microsoft.Dynamics.Nav.Runtime.NavDotNet.InvokeMethod[T](Boolean isStatic, String methodName, UInt32 methodIndex, Object[] arguments)
at Microsoft.Dynamics.Nav.Runtime.NavDotNet.InvokeMethod[T](String methodName, UInt32 methodIndex, Object[] arguments)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit502.AcquireTokensWithCertificate_1022959791(NavText redirectURL, NavText clientId, NavSecretText certificate, NavSecretText certificatePassword, NavText oAuthAuthorityUrl, NavList1 scopes, ByRef1 accessToken, ByRef1 idToken) at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit501.AcquireTokensWithCertificate_1852588485(NavText clientId, NavSecretText certificate, NavSecretText certificatePassword, NavText redirectURL, NavText oAuthAuthorityUrl, NavList1 scopes, ByRef1 accessToken, ByRef1 idToken)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2361.AcquireTokenWithCertificate(ByRef1 accessToken, ByRef1 errorText)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2361.GetToken()
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2361.GetAuthorizationHeaders()
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2351.Authorize(NavCodeunitHandle httpRequestMessage)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2351.SendRequest(NavCodeunitHandle httpRequestMessage, NavCodeunitHandle httpResponseMessage)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2351.Send_1432468718(NavScope γReturnValueParent, NavCodeunitHandle httpRequestMessage)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2351.Send_1769361975(NavScope γReturnValueParent, NavOption method, NavText requestUri, NavCodeunitHandle content)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2351.Send(NavScope γReturnValueParent, NavOption method, NavText requestUri)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2350.Send(NavScope γReturnValueParent, NavOption method, NavText requestUri)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit2350.Get(NavScope γReturnValueParent, NavText requestUri)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit9354.Get(NavScope γReturnValueParent, NavCodeunitHandle graphUriBuilder, NavCodeunitHandle graphOptionalParameters)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit9351.Get_1956849062(NavText relativeUriToResource, NavCodeunitHandle graphOptionalParameters, NavCodeunitHandle httpResponseMessage)
at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit9350.Get_1956849062(NavText relativeUriToResource, NavCodeunitHandle graphOptionalParameters, NavCodeunitHandle httpResponseMessage)
at Microsoft.Dynamics.Nav.BusinessApplication.Page50900.GetSiteLists(NavCodeunitHandle graphClient)
at Microsoft.Dynamics.Nav.BusinessApplication.Page50900.InitialiseGraphClientCertificate_a45_OnAction()
at Microsoft.Dynamics.Nav.Runtime.NavApplicationMethod.InvokeMethodAsync(NavApplicationObjectBase applicationObject, String methodName, Object[] args, Boolean resolveHandler, Boolean throwOnNotFound)
at Microsoft.Dynamics.Nav.Runtime.NavForm.RaiseOnActionAsync(Nullable1 actionId, String actionTriggerMethodName, Object[] args) at Microsoft.Dynamics.Nav.Service.NSField.<>c__DisplayClass21_0.<<ActionFieldAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.Dynamics.Nav.Service.NsFormDataAccess.RunWithFormDataAccessAsync[T](ITreeObject parent, NavForm form, Func2 func)
at Microsoft.Dynamics.Nav.Service.NsDataAccess.RunWithDataAccessAsync[T](ITreeObject parent, NavRecordState state, Boolean instantiateNewForm, Guid parentFormHandle, String parentControlName, Func2 func) at System.Threading.Tasks.ValueTask1.GetTaskForValueTaskSource(IValueTaskSource1 t) --- End of stack trace from previous location --- at System.Threading.Tasks.ValueTask1.GetTaskForValueTaskSource(IValueTaskSource1 t) --- End of stack trace from previous location --- at Microsoft.Dynamics.Nav.Service.AspNetCore.ClientService.ServiceOperationPipelineHelper.CallWithHandlingTargetInvocationException[T](Func1 asyncFunc)
at Microsoft.Dynamics.Nav.Service.ServiceOperationPipeline.StartSamplingProfilingCombinator[T](ServiceOperationContext context, ServiceOperation1 innerOperation) at Microsoft.Dynamics.Nav.Service.ServiceOperationPipeline.AddSessionToThreadSchedulerCombinator[T](ServiceOperationContext context, ServiceOperation1 innerOperation)
at Microsoft.Dynamics.Nav.Service.ServiceOperationPipeline.RunInTransactionCombinator[T](ServiceOperationContext context, ServiceOperation1 innerOperation) at Microsoft.Dynamics.Nav.Service.ServiceOperationPipeline.TransientErrorRetryCombinator[T](ServiceOperationContext context, ServiceOperation1 innerOperation)
at Microsoft.Dynamics.Nav.Service.ServiceOperationPipeline.ErrorMappingCombinator[T](ServiceOperationContext context, ServiceOperation`1 innerOperation, Boolean useCoreWcfFaultException)

Type: System.ArgumentException
Source: System.Security.Cryptography
HResult: -2147024809
StackTrace:
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)
at Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.CertificateFromBase64(String certificateBytesBase64, String certificatePassword)
at Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificateAsync(String clientId, NavSecretText certificate, NavSecretText certificatePassword, String authority, String[] scopes)
at Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificate(String clientId, NavSecretText certificate, NavSecretText certificatePassword, String authority, String[] scopes)
at InvokeStub_ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificate(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithManyArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

Expected behavior

When calling GraphAuthorization.CreateAuthorizationWithClientCredentials using the certificate overload, the provided Certificate and CertificatePassword should be correctly assigned to the internal global variables of Codeunit 9357 "Graph Auth. Client Credentials". This should result in a valid Http Authentication interface that allows the Graph Client to acquire an access token from Microsoft Entra ID, matching the successful behavior of the Client Secret authentication flow.

Steps to reproduce

Generate a Base64 encoded PFX certificate.

Call GraphAuthorization.CreateAuthorizationWithClientCredentials using a Tenant ID, Client ID, Certificate (SecretText), and Password (SecretText).

Attempt to use the resulting Graph Client to make any API call (e.g., GraphClient.Get(...)).

Actual Behavior: The call fails with the error: A call to Microsoft.Dynamics.Nav.Runtime.ALAzureAdCodeGrantFlow.ALAcquireApplicationTokensWithCertificate failed with this message: Array may not be empty or null. (Parameter 'rawData').

Root Cause (The Bug): In Codeunit 9357, the SetParameters procedure contains the following line: Certificate := Certificate; It should be: Certificate := NewCertificate;

Reproduced In:

Business Central 2024 Release Wave 2 (BC25), BC27.1

Additional context

No response

I will provide a fix for a bug

  • I will provide a fix for a bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    IntegrationGitHub request for Integration area

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions