Service to service authentication in Business Central 18.3 – How to set up

6 Jul

In the previous blog post, I’ve described the usage scenarios around OAuth client credentials flow for Business Central. In this post, I want to show how to set up this new feature. The next blog post will contain code examples of how to use it.

Please note that the steps explained below already work in the current version, but you need to wait for version 18.3 to be able to actually call the APIs.

The official documentation can be found here, which includes similar information. I’ll just try to clarify some of the steps and provide some screenshots.

The process consist of three steps:

  • Register the external application in Azure Active Directory
  • Create the external application account in Business Central
  • Grant consent

Note: In the text below “application” means “the external application, accessing Business Central APIs”.

Step 1: Register the external application in Azure Active Directory

Business Central uses Azure AD for Identity and Access Management. This means that users accessing Business Central are stored and managed in Azure AD. If an external application needs to access Business Central, it also needs its own identity. When you register your application with Azure AD, you’re creating an identity configuration for your application that allows it to integrate with Azure AD.

An Azure AD application is defined by its one and only application object, which resides in the Azure AD tenant where the application was registered (known as the application’s “home” tenant). An application object is used as a template or blueprint to create one or more service principal objects. A service principal is created in every tenant where the application is used. What a service principal is will be explained later in this article.

Registering the application is done by the organization owning the application that will call the Business Central APIs in their home tenant. The registration consists of these steps:

  • Create the application in Azure
  • Set the required permissions
  • Create a secret

Create the application in Azure

An important choice you need to make here is if the application will be single tenant or multitenant.

If the application will only be used inside the same organization, then you should choose single tenant. For example an in-house developed portal, or a self-hosted webshop. But if you develop an application that will be used by other organizations to integrate with their Business Central environment, then you should choose multitenant.

Navigate to Azure portal and open Azure Active Directory. Click on App Registrations in the menu and then on New registration. Fill in a name and choose the supported account type. Only choose from single tenant or multitenant, don’t look at the options that include personal Microsoft accounts as these are not supported by Business Central.

Under Redirect URI choose Web and then fill in this URL: Please note that this property is case-sensitive!

Click on Register to create the application. Copy the Application (client) ID from the overview screen to a text file. You will need this later when registering the application in Business Central and when calling the APIs.

Set the required permissions

The next step is to set the API permissions that the external application needs. Click on API permissions in the menu and then on Add a permission. From the list of commonly used Microsoft APIs, you select Dynamics 365 Business Central. Since the app is going to have its own account in Business Central, you must select Application permissions. As the description says, this is for applications that run as a background service without a signed-in user. See the previous blog post for more information about the difference between delegated permissions and application permissions.

There are three permissions available:

To access all Business Central APIs, we need to select the API.ReadWrite.All permission. Please note that this does not mean that the application will be able to unlimitedly read and write data with all APIs. The actual access to data is limited by the permissions that are assigned to the application account in Business Central.

Click Add permissions to save the settings. You should now see a screen like this:

Under status, you can see that the newly added permission has not been granted for the current organization. If you are registering a single tenant application, then it would be an option to click on the Grant admin consent button. This also makes sense if you are registering a multitenant application that will be used in your own organization as well. In all other cases, when the application will be used by another organization, then they need to grant access from Business Central. Which will be explained below.

Create a secret

The last step in registering the app in Azure is to create a secret. Click on Certificates & secrets in the menu and then click on New client secret. Choose whatever expiration period you want and click on Add. Don’t forget to copy the created secret because this is the only time you will be able to see it.

Tip: it’s not possible to set an unlimited expiration period. The longest period is 24 months. This means that you need to update the secret every now and then.

That’s it, you are now ready with the first step to register the application in Azure. The next step is to create the application account in Business Central.

Step 2: Create the external application account in Business Central

There are two ways to create the external application account in Business Central: manually or automatically from code. In both cases, the app needs to be granted consent manually, but the option to create the record from code is of course much more convenient for end-users. Let’s first see how to manually create the application account.

Manually create the Azure Active Directory Application account

In Business Central search for Azure Active Directory Applications (or just AAD) and open the page. Click on New to add a new record. Fill in the Client Id that you copied in the previous step or received from the organization that owns the external application. The curly brackets will be added automatically. A description is also needed.

On this card page, we can also configure the permissions. These permissions will be applied to all API sessions of the application account. In the example below the external application is assigned the permission set D365 BASIC and D365 SALES DOC, EDIT.

Create the Azure Active Directory Account from code

Let’s now see how the Azure AD account can be created from code in Business Central. Creating the account, including permissions, is much easier for the customer of course. And it would make perfect sense to combine this with an app that also includes custom APIs. The code could be executed automatically, during installation, or from an action on a setup page.

The BC base app provides an interface to create the AAD application from code with codeunit “AAD Application Interface”. This will create a record in the table “AAD Application”. It’s not a complete interface, it only supports creating the app and optionally enable it. It does not set the Extension information automatically, so you need to add it after the record has been created. It also doesn’t support adding permission sets, so we need to do it ourselves.

To assign permissions sets a corresponding user record must be created in the User table. If we use the function CreateAADApplication without setting the parameter EnableAADApplication to true, then there will be no user record created. Because we can’t assign permissions to a non-existing user, we should enable the AAD application first. Of course, you can set the status to disabled afterward, that is no problem.

If you call the code from an install codeunit, then make sure to use the OnInstallAppPerDatabase trigger because the table “AAD Application” is database-wide, and not per company.

Here is an example of an install codeunit and two codeunits that creates the AAD Application record, updates it with extra info, and assigns permission sets.

codeunit 50100 InstallAJK
    Subtype = Install;
    trigger OnInstallAppPerDatabase()
    local procedure CreateAADApplications()
        WebshopIntegrationAADSetup: Codeunit WebshopIntegrationAADSetupAJK;
codeunit 50101 WebshopIntegrationAADSetupAJK
        WebshopIntegrationClientIdTok: Label '3870c15c-5700-4704-8b1b-e020052cc860', Locked = true;
        WebshopIntegrationDescriptionTxt: Label 'Integration with the award winning webshop';
    procedure CreateWebshopIntegrationAADApplication()
        AADApplicationInterface: Codeunit AADApplicationInterfaceAJK;
        AppInfo: ModuleInfo;
        ClientDescription: Text[50];
        ContactInformation: Text[50];
        ClientDescription := CopyStr(WebshopIntegrationDescriptionTxt, 1, MaxStrLen(ClientDescription));
        ContactInformation := CopyStr(AppInfo.Publisher, 1, MaxStrLen(ContactInformation));
    local procedure GetPermissionSets() PermissionSets: List of [Code[20]]
        PermissionSets.Add('D365 BASIC');
        PermissionSets.Add('D365 SALES DOC, EDIT');
    local procedure GetPermissionGroups() PermissionGroups: List of [Code[20]]

    local procedure GetWebshopIntegrationClientId() Id: Guid
        Id := WebshopIntegrationClientIdTok;
codeunit 50102 AADApplicationInterfaceAJK
    procedure CreateAADApplication(
        ClientId: Guid;
        ClientDescription: Text[50];
        ContactInformation: Text[50];
        AppInfo: ModuleInfo;
        PermissionSets: List of [Code[20]];
        PermissionGroups: List of [Code[20]])
        AADApplication: Record "AAD Application";
        AADApplication := InsertAADApplication(ClientId, ClientDescription, ContactInformation, AppInfo);
        AssignUserGroupsToAADApplication(AADApplication, PermissionGroups);
        AssignPermissionsToAADApplication(AADApplication, PermissionSets);
    local procedure InsertAADApplication(
        ClientId: Guid;
        ClientDescription: Text[50];
        ContactInformation: Text[50];
        AppInfo: ModuleInfo) AADApplication: Record "AAD Application"
        AADApplicationInterface: Codeunit "AAD Application Interface";
            ClientId, ClientDescription, ContactInformation, true);
        AADApplication."App ID" := AppInfo.PackageId;
        AADApplication."App Name" := AppInfo.Name;
    local procedure AssignUserGroupsToAADApplication(var AADApplication: Record "AAD Application"; UserGroups: List of [Code[20]])
        UserGroupCode: Text;
        if not UserExists(AADApplication) then
        foreach UserGroupCode in UserGroups do
            AddUserToGroup(AADApplication."User ID", UserGroupCode, '')
    local procedure AssignPermissionsToAADApplication(var AADApplication: Record "AAD Application"; PermissionSets: List of [Code[20]])
        PermissionSetName: Text;
        if not UserExists(AADApplication) then
        foreach PermissionSetName in PermissionSets do
            AddPermissionSetToUser(AADApplication."User ID", PermissionSetName, '');
    local procedure AddUserToGroup(UserSecurityID: Guid; UserGroupCode: Code[20]; Company: Text[30])
        UserGroupMember: Record "User Group Member";
        UserGroupMember.SetRange("User Security ID", UserSecurityID);
        UserGroupMember.SetRange("User Group Code", UserGroupCode);
        UserGroupMember.SetRange("Company Name", Company);
        if not UserGroupMember.IsEmpty() then
        UserGroupMember."User Security ID" := UserSecurityID;
        UserGroupMember."User Group Code" := UserGroupCode;
        UserGroupMember."Company Name" := Company;
    local procedure AddPermissionSetToUser(UserSecurityID: Guid; RoleID: Code[20]; Company: Text[30])
        AccessControl: Record "Access Control";
        AccessControl.SetRange("User Security ID", UserSecurityID);
        AccessControl.SetRange("Role ID", RoleID);
        AccessControl.SetRange("Company Name", Company);
        if not AccessControl.IsEmpty() then
        AccessControl."User Security ID" := UserSecurityID;
        AccessControl."Role ID" := RoleID;
        AccessControl."Company Name" := Company;
    local procedure UserExists(var AADApplication: Record "AAD Application") Result: Boolean
        User: Record User;
        if IsNullGuid(AADApplication."User ID") then
        Result := User.Get(AADApplication."User ID");

The code above creates the AAD Application record as shown below. The only instruction for the user would be to open the record and click on Grant Consent.

The web application must be represented in Azure AD by a service principal. The application object created in step 1 is the global representation of the application for use across all tenants, and the service principal is the local representation for use in a specific tenant. The application object serves as the template from which common and default properties are derived for use in creating corresponding service principal objects.

A service principal must be created in each tenant where the application is used, enabling it to establish an identity for sign-in and/or access to Business Central being secured by the tenant. The process is called “grant consent”. This means that we tell Azure AD that the application is allowed to access Business Central, which is then stored as the service principal representing the consented application.

Click on the Grant Consent action at the top in the Azure Active Directory Application card in Business Central. It will open a popup page that requires you to log in. The user that gives consent needs to be a Global Administrator, an Application Administrator, or a Cloud Application Administrator. If you don’t have that role, then let a user who has the required role log in here. This is only needed in this step of the registration process. After logging in you will get a page that asks to accept the permission requested by the application.

Please note the word unverified in the screenshot below, under the name of the application. That is because I didn’t associate an MPN account with the Azure app registration. It’s highly recommended to associate the MPN account of your organization under Branding in the Azure app registration to get your company displayed.

Click on Accept and the page will close and you will get a confirmation message in Business Central.

Now the external application has been fully set up to access Business Central APIs.


After finishing all these steps, several components have been created in Azure AD and Business Central. Because a picture says more than a thousand words, here is the story again:

In this picture:

Now you made it this far, it would also be a good idea to read the official documentation: As you will see, I’ve been using parts of the documentation in this article.

What if the external application is not used anymore? How do you block access to Business Central? Well, that’s quite easy. Open the AAD Application card and set State to Disabled. That will also set the corresponding user account in the User table to disabled. Or you delete the complete AAD Application record, which will also disable the corresponding user account.

However, when you granted consent to the AAD Application, a service principal was also created in Azure AD. Deleting the AAD Application in Business Central does not delete the service principal in Azure AD.

To revoke consent in Azure, you need to find the service principal in Azure AD. This is a procedure that must be done by an administrator. This procedure can be done through Azure Active Directory PowerShell or the Microsoft Graph API, but the easiest way for the average administrator is right through the Azure portal.

Open the Azure Portal at and navigate to the Enterprise Applications blade.

Then click on “All Applications” and search for the application you want to revoke consent for.

Click on the application to open the Overview section of the application. Then click on Permissions in the left menu to review the permissions for this application. It should show Dynamics 365 Business Central and Full access to web services API.

If you have reviewed and are sure that this is the application you want to revoke consent for, click on Properties in the left menu. On the properties page click on Delete. By deleting the application here you remove all its OAuth permission grants in this Active Directory. Think about it as uninstalling the application.

That’s it! The application and all consent associated with the application are now gone. Perform the Grant Consent procedure in Business Central to get it back, which will recreate the service principal in Azure AD.

The next blog post will contain code examples of how to call the Business Central APIs using client credentials flow.

34 thoughts on “Service to service authentication in Business Central 18.3 – How to set up

  1. I create azure AD Application manual direct on related page. I can access API Web Service Entities Companies, but in other entities (example : item), i can not access. The error message :
    You do not have the following permissions on Page APIV2 – Items: Execute.\r\n\r\nTo view details about your permissions, see the Effective Permissions page
    I already set permission set D365 BASIC ,D365 SALES DOC. EDIT
    Any clue for my issue?

    • This happens when you try it out on version 18.2. This feature is available from the next version 18.3. Create a new sandbox with 18.3 and try again.

  2. Thanks for taking the time to describe these OData2 flows.

    Will Service to service authentication work with other ODataV4 sources besides API’s? (i.e. queries and pages that are published as webservices)?
    Will it also work with ODataV3 and SOAP?
    Gert Lynge

    • Yes, it should work with all web service types. Just keep in mind that ODataV3 as been deprecated. And SOAP UI Pages has also been obsoleted already.

  3. I had the same kind of error with BC 18.3 and my solution was to assign my app service principal to the AdminAgents group like the following PowerShell (5.1) script :

    $aadAppId = “Your Application ID”
    $adminAgentsGroup = Get-AzureADGroup -Filter “displayName eq ‘AdminAgents'”
    $servicePrincipal = Get-AzureADServicePrincipal -Filter “appId eq ‘$aadAppId'”
    Add-AzureADGroupMember -ObjectId $adminAgentsGroup.ObjectId -RefObjectId $servicePrincipal.ObjectId

    • Thanks for the addition.

      Be careful, the Admin Agents group is assigned to the Global Administrator role in the Azure AD. I don’t think this should be necessary. At least I’ve never done this, in all my tests it worked just as expected.

      Instead you could also try to delete the service principal and recreate it by granting consent.

  4. Pingback: Using Postman to test OAuth 2.0 authorization to Business Central restful API - Dynamics 365 Business Central Community

  5. Hi Arend-Jan

    Thanks for this great blog post – I could successfully implement it as described.

    Do you know where I can increase the bearer token expiration from default 1 hour to e.g. 24 hours?



  6. This is my version Business Central 18.3 (Platform 18.0.27224.28966 + Application 18.3.27240.27381) still getting the same error. Can you please help?

    • You have probably missed one of the steps. I can’t say which one, it might be the wrong permission (delegated instead of application) or the give consent step was not done. Or the app registration in BC has no permissions.

  7. You do not have the following permissions on Page APIV2 – Items: Execute.\r\n\r\nTo view details about your permissions, see the Effective Permissions page.

  8. Pingback: Service to service authentication in Business Central 18.3 – How to set up – Kauffmann @ Dynamics 365 Business Central – Thinking Enterprise Solutions

  9. Hi Arend-Jan,
    Great series of blog posts! How things are for on-prem world? I hardly find any clarifications about it. Even in the official documentation ( it says “APPLIES TO: Business Central Online only”.

    We really are having headache now migrating our websites that should are compatible with both Cloud and On Prem. On one hand on-prem basic authentication is having performance issue with version 19.0 (version 18.3 had at least a working work around and we are not sure if we can have oAuth for on prem as well.

    Thanks in advance!


    • Sorry, didn’t see your question earlier. I’m currently working on a blog post (series most probably) to enable OAuth for on-prem.

  10. Hi, I have the same problem. I get a token, but I have a 401 Unauthorized error with this url. Can someone help me?

  11. Hi AJ, thank you for sharing this article.. I tried the codeunit as an example to creates the AAD Application record but as soon as i try to deploy the extension. I get following error:

    You need to have either SECURITY or SECURITY privileges in the user permission set to update the state.’ and AL stack trace:
    “AAD Application”(Table 9012).”State – OnValidate”(Trigger) line 13

    I also added SECURITY role to my user but it still didn’t worked.
    Am i missing something here?

    • Haven’t seen that error myself, need to investigate. It depends on the user context during app installation. Maybe something has changed since I posted this blog?

  12. Hi AJ,

    I followed your complete tutorial and this is working on our tenant.
    However, when i add the application in another tenant and grant consent, I dont get a valid token back.
    I also don’t see a service principal in the Azure environment from this tenant.

    Do you have any idea what might be the problem?

    Kind regards.

    • I guess you have registered the app as multitenant in Azure, right?
      When you request the token, do you provide the target Azure AD Tenant ID in the URL?{AadTenantId}/oauth2/v2.0/token –> {AadTenantId} should be the other tenant, not the tenant where the app is registered.

      • Thank you for your response!

        Yes, I used the id from the customers tenant. I tried both the ID as well as the name (…

        In my other comment I already mentioned that Gsimard’s solution helped me, adding the app to the admin group.

        Any idea why this could be?

        • No idea, I never did that and it worked for me across tenants. It’s weird that others are seeing this problem, but I don’t. Maybe I was using an admin user to give consent, while others did not? Just guessing…

          • Indeed weird 🤔
            Anyway, thanks again for the blogpost. It was extremely usefull!

  13. Hi again,

    I used gsimard’s suggestion and this also solved my problem.

    I to have no idea why I must add our app to the admin group, but at least it works now.

    Thank you for this amazing blogpost!

    Kind regards.

  14. Pingback: Business Central OAuth2 for Power Automate 🛂 | Josh Anglesea

  15. Hi.

    Do you know if the service-to-service is connecting only to the BC default or custom APIs is in need of certificate-based authentication?
    Or is it still accepted with client credentials?

    Wondering because I think I’ve heard that the APIs is building upon Dataverse and any integrations to Dataverse is no longer accepting client credentials around March/April 2022

  16. That would be awesome. On 19.1 on-premises I can’t make it work, it’s always rejecting calls with “The server has rejected the client credentials”, but I’m not sure if it’s a problem in my configuration or simply not supported in this version. With 19.5 there should have been added some features around that topic, but I just can’t find anything about that…

  17. Is there any update on this for 22.0 and later where the “User Group” and “User Group Member” are marked for removal. We should instead use the new Security Groups, but I cannot figure out how to use those from the code as table “Security Group” is protected and the codeunit “Security Group” has no set/add methods for adding users to a security group.

  18. Small adjustment, within BC23 you can’t find Azure Active Directory anymore, name is changed to Microsoft Entra Application

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.