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

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: https://businesscentral.dynamics.com/OAuthLanding.htm. 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()
    begin
        CreateAADApplications();
    end;

    local procedure CreateAADApplications()
    var
        WebshopIntegrationAADSetup: Codeunit WebshopIntegrationAADSetupAJK;
    begin
        WebshopIntegrationAADSetup.CreateWebshopIntegrationAADApplication();
    end;
}

codeunit 50101 WebshopIntegrationAADSetupAJK
{
    var
        WebshopIntegrationClientIdTok: Label '3870c15c-5700-4704-8b1b-e020052cc860', Locked = true;
        WebshopIntegrationDescriptionTxt: Label 'Integration with the award winning webshop';

    procedure CreateWebshopIntegrationAADApplication()
    var
        AADApplicationInterface: Codeunit AADApplicationInterfaceAJK;
        AppInfo: ModuleInfo;
        ClientDescription: Text[50];
        ContactInformation: Text[50];
    begin
        NavApp.GetCurrentModuleInfo(AppInfo);
        ClientDescription := CopyStr(WebshopIntegrationDescriptionTxt, 1, MaxStrLen(ClientDescription));
        ContactInformation := CopyStr(AppInfo.Publisher, 1, MaxStrLen(ContactInformation));

        AADApplicationInterface.CreateAADApplication(
            GetWebshopIntegrationClientId(),
            ClientDescription,
            ContactInformation,
            AppInfo,
            GetPermissionSets(),
            GetPermissionGroups());
    end;

    local procedure GetPermissionSets() PermissionSets: List of [Code[20]]
    begin
        PermissionSets.Add('D365 BASIC');
        PermissionSets.Add('D365 SALES DOC, EDIT');
    end;

    local procedure GetPermissionGroups() PermissionGroups: List of [Code[20]]
    begin
    end;


    local procedure GetWebshopIntegrationClientId() Id: Guid
    begin
        Id := WebshopIntegrationClientIdTok;
    end;
}

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]])
    var
        AADApplication: Record "AAD Application";
    begin
        AADApplication := InsertAADApplication(ClientId, ClientDescription, ContactInformation, AppInfo);
        AssignUserGroupsToAADApplication(AADApplication, PermissionGroups);
        AssignPermissionsToAADApplication(AADApplication, PermissionSets);
    end;

    local procedure InsertAADApplication(
        ClientId: Guid;
        ClientDescription: Text[50];
        ContactInformation: Text[50];
        AppInfo: ModuleInfo) AADApplication: Record "AAD Application"
    var
        AADApplicationInterface: Codeunit "AAD Application Interface";
    begin
        AADApplicationInterface.CreateAADApplication(
            ClientId, ClientDescription, ContactInformation, true);

        AADApplication.Get(ClientId);

        AADApplication."App ID" := AppInfo.PackageId;
        AADApplication."App Name" := AppInfo.Name;
        AADApplication.Modify();
    end;

    local procedure AssignUserGroupsToAADApplication(var AADApplication: Record "AAD Application"; UserGroups: List of [Code[20]])
    var
        UserGroupCode: Text;
    begin
        if not UserExists(AADApplication) then
            exit;

        foreach UserGroupCode in UserGroups do
            AddUserToGroup(AADApplication."User ID", UserGroupCode, '')
    end;

    local procedure AssignPermissionsToAADApplication(var AADApplication: Record "AAD Application"; PermissionSets: List of [Code[20]])
    var
        PermissionSetName: Text;
    begin
        if not UserExists(AADApplication) then
            exit;

        foreach PermissionSetName in PermissionSets do
            AddPermissionSetToUser(AADApplication."User ID", PermissionSetName, '');
    end;

    local procedure AddUserToGroup(UserSecurityID: Guid; UserGroupCode: Code[20]; Company: Text[30])
    var
        UserGroupMember: Record "User Group Member";
    begin
        UserGroupMember.SetRange("User Security ID", UserSecurityID);
        UserGroupMember.SetRange("User Group Code", UserGroupCode);
        UserGroupMember.SetRange("Company Name", Company);

        if not UserGroupMember.IsEmpty() then
            exit;

        UserGroupMember.Init();
        UserGroupMember."User Security ID" := UserSecurityID;
        UserGroupMember."User Group Code" := UserGroupCode;
        UserGroupMember."Company Name" := Company;
        UserGroupMember.Insert(true);
    end;

    local procedure AddPermissionSetToUser(UserSecurityID: Guid; RoleID: Code[20]; Company: Text[30])
    var
        AccessControl: Record "Access Control";
    begin
        AccessControl.SetRange("User Security ID", UserSecurityID);
        AccessControl.SetRange("Role ID", RoleID);
        AccessControl.SetRange("Company Name", Company);

        if not AccessControl.IsEmpty() then
            exit;

        AccessControl.Init();
        AccessControl."User Security ID" := UserSecurityID;
        AccessControl."Role ID" := RoleID;
        AccessControl."Company Name" := Company;
        AccessControl.Insert(true);
    end;

    local procedure UserExists(var AADApplication: Record "AAD Application") Result: Boolean
    var
        User: Record User;
    begin
        if IsNullGuid(AADApplication."User ID") then
            exit;

        Result := User.Get(AADApplication."User ID");
    end;
}

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.

Step 3: 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.

Overview

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: https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals. As you will see, I’ve been using parts of the documentation in this article.

Revoke consent

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 https://portal.azure.com 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.

Comment List
Related
Recommended