1

Here is my MSAL authentication:

@app.route('/get-microsoft-data', methods=('GET', 'POST'))
def get_microsoft_token():
    public_app = ConfidentialClientApplication(
        client_id="<client_id>", authority="https://login.microsoftonline.com/<tenant_id>",
        client_credential="<client_secret>"
    )
    
    result = None
    result = public_app.acquire_token_silent(["https://api.businesscentral.dynamics.com/.default"], account=None)

    if not result:
        print("No suitable token exists in cache. Let's get a new one from AAD.")
        result = public_app.acquire_token_for_client(scopes=["https://api.businesscentral.dynamics.com/.default"])
        
    if "access_token" in result:
        global microsoft_token
        microsoft_token = result["access_token"]

    return redirect('/') 

This is my call to business central api:

@app.route('/send-data-to-microsoft', methods=('GET', 'POST'))
def send_data_to_microsoft():
    print(microsoft_token)
    
    headers = {
        "Authorization": "Bearer " + microsoft_token
    }
    
    r = requests.get("https://api.businesscentral.dynamics.com/v1.0/<tenant_domain>/sandbox/api/v1.0/companies", headers=headers)
    print(r.json())
    return redirect('/')

And this is the error I'm getting when I call /send-data-to-microsoft:

{'error': {'code': 'Authentication_InvalidCredentials', 'message': 'The server has rejected the client credentials.  CorrelationId:  ff4d9d32-db03-4c2a-bf77-2e6186d4988c.'}}

This is the doc for what I want: https://learn.microsoft.com/en-us/dynamics-nav/api-reference/v1.0/api/dynamics_companies_get

This is the list of valid endpoints for business central: https://learn.microsoft.com/en-us/dynamics-nav/api-reference/v1.0/endpoints-apis-for-dynamics

Jack Riley
  • 71
  • 1
  • 8
  • Also I granted app_access for this application's business central api permissions. So I'm pretty stumped. Especially cause calls to Microsoft graph work fine with this token. – Jack Riley Jul 26 '20 at 17:23
  • the best thing you can do is compare both requests (graph / code) after acquiring the token – Thiago Custodio Jul 26 '20 at 17:42

2 Answers2

5

Client credential flow is NOT supported here. The supported Authentication methods for Dynamics 365 BC are only these 2 options based on the official document:

  • Basis Authentication
  • AAD authentication

If you want to call D365 BC API with a method which doesn't require user interaction, you should choose Basis Authentication.

The steps to do it are:

  1. To set up basic authentication, log into your tenant, and in the Search field, enter Users and then select the relevant link.
  2. Select the user to add access for, and on the User Card page, in the Web Service Access Key field, generate a key.
  3. Copy the generated key and use it as the password for the username.

And then you could refer to Exploring the APIs with Postman and basic authentication.

Allen Wu
  • 15,529
  • 1
  • 9
  • 20
  • Thank you so much Allen, maybe I completely skimmed the docs but that wasn't clear at all to me. I think that I didn't understand that you can't use MSAL authentication with Business Central. Could I still use AAD with a get request do you think? – Jack Riley Jul 27 '20 at 15:04
  • 1
    @Jack AAD authentication is supported only for auth code flow. But you are trying to use Client credential flow. If you want to change to use auth code flow you can see https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-develop-connect-apps#AAD and https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-develop-connect-apps#exploring-the-apis-with-postman-and-aad-authentication. – Allen Wu Jul 28 '20 at 05:03
  • 1
    @Jack Besides, if you still don't want a user interaction, you should use Basis Authentication. You can refer to this [post](https://stackoverflow.com/questions/6999565/python-https-get-with-basic-authentication) to learn how to use Basis Authentication with Python. – Allen Wu Jul 28 '20 at 05:10
  • Ms depreciated basic auth – fartwhif Dec 22 '22 at 15:53
1

2021-10-12 Update: The client credential flow is now supported in BC.

There's a little additional setup that's required. The following link will walk you through it:

https://www.kauffmann.nl/2021/07/06/service-to-service-authentication-in-business-central-18-3-how-to-set-up/

It boils down to:

  1. Register the external application in Azure Active Directory
    • create the application in Azure (paying particular attention to the Redirect URI)
    • set the required permissions ("Dynamics 365 Business Central / API.ReadWrite.All")
    • create a secret
  2. Create the external application account in Business Central
    • add the Client ID from your registered Azure app
    • add permissions (e.g., "D365 BASIC" and "D365 SALES DOC, EDIT")
    • grant consent

at that point you can get a token with:

curl --location --request GET 'https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=<client>' \
--data-urlencode 'scope=https://api.businesscentral.dynamics.com/.default' \
--data-urlencode 'client_secret=<secret>'

And the customer query works as:

curl --location --request GET 'https://api.businesscentral.dynamics.com/v2.0/<tenant>/<env>/api/v2.0/companies(<company id>)/customers' \
--header 'Authorization: Bearer XYZ...'

Incidentally, I was getting the same Authentication_InvalidCredentials error and it turned out to be tied to the external app in BC not being active.