After configuring Business Central on-premises for Azure Active Directory authentication, as explained in the previous blog post, it’s now time to configure it for OAuth authentication with APIs and web services.
Two options
There are two options that you can choose from. An easy option that requires only one setting and a more complex option that requires extra configuration in Azure. Let me first explain the more complex option and when you’ve seen that I’m sure you will love the easy option.
Option 1: Custom scope
As you may know, calling Business Central APIs on the SaaS platform with OAuth authentication needs a so-called scope. The scopes we can choose from are:
The scope consists of two parts: a resource followed by a permission or role. The resource is the full Application ID URI that is defined in the Azure app registration. In the previous blog post the example Application ID URI was defined as https://businesscentral.cronus.company. To get the full scopes for the Business Central on-premises environment, we just need to replace the standard resource with our custom resource. The scopes will then be:
- https://businesscentral.cronus.company/user_impersonation
- https://businesscentral.cronus.company/API.ReadWrite.All
- https://businesscentral.cronus.company/Automation.ReadWrite.All
Note that I’ve skipped the Financials.ReadWrite.All permission. That’s only to be used with the Microsoft Graph endpoint and doesn’t apply to an on-premises environment.
These scopes need to be defined in Azure before we can use them. This is done in two places. Delegated scopes are defined under Expose an API and application scopes are defined under App roles.
Configure delegated permissions
Go to the app registration in Azure and click on Expose an API in the left menu. At the top of this page, you will see the defined Application ID URI. Click on Add a scope and define a new scope user_impersonation as follows:
Don’t forget to click on Add scope to finish this step. The screen now shows the added scope.
Now we need to test this permission by retrieving an OAuth access token and calling an API on our Business Central server instance. Let’s do this with Postman.
The first step is to create another app registration that represents our Postman installation. We do this in the same Azure Active Directory tenant as where the Business Central service tier has been created. Open Azure Active Directory, click on App registrations and then on New registration at the top. Give the app a name, e.g. Postman. Leave the other settings at the default values. Click on Register to create the app registration.
Make a copy of the Application (client) ID on the overview page. We need this value for Postman.
Click on Authentication in the left menu. Click on Add a platform and then on Mobile and desktop applications. Select the first default redirect URI: https://login.microsoftonline.com/common/oauth2/nativeclient. Finally, click on Configure. The Authentication page should show the selected redirect URI.
Make a copy of the redirect URI because we need this in Postman.
This is all we need to do for the app registration of Postman. You may think, what about setting the permission? Well, that’s not required because we will specify the required permission in Postman. That’s called dynamic permissions. And what about creating a secret? Again, not required because we are using a public client that can’t hold a secret. If you want to know more about this, then you may want to watch this video.
Test delegated permissions with Postman
Open Postman and create a new request. The URL of the request looks like: https://prod.cronus.company:7048/BC/api/v2.0. Open the Authorization tab and choose type OAuth 2.0. Then under Configure New Token specify these values:
Click on Get New Access Token and log in with your Azure AD account. Now you should get a window that asks for your permission to allow Postman to access Business Central on your behalf.
Click on Accept and then Postman will finish the flow by retrieving the access token. If that is successful, then you will get a window in Postman with the access token. Click on Use Token to select this token for the API request.
That’s it. Hit the Send button to call your Business Central environment with OAuth authentication!
As you probably noticed, we didn’t even touch the Business Central server instance. The settings we did in the previous blog post to enable Azure AD authentication were sufficient to enable the OAuth for delegated user permissions.
Service to service authentication
But we are not done yet. This was the delegated user permissions, officially called Authorization Code grant flow. We have something called service-to-service authentication, also known as Client Credentials flow. That is a permission that lets an external application use its own account rather than the logged-in user. Let’s configure that as well. Fasten your seatbelts, this requires a little more configuration!
Open the Azure app registration for the Business Central application. In the left menu click on App roles. Click on Create app role at the top. Define the app role as follows:
Click on Apply to create the app role. The App roles page will now look like this.
The next step is to add this permission to the Postman app registration. Application permissions can’t be added dynamically as we did with the delegated permissions earlier. Application permissions must be specified as static permission on the app registration.
Open the Postman app registration in Azure and click on API permissions in the left menu. This page should currently have one configured permission: User.Read. Click on Add a permission above the list of configured permissions. Then click on My APIs at the top. The list should contain the name of the app registration for the Business Central application. Select this entry.
Select Application permissions and then select API.ReadWrite.All.
Click on Add permissions to add the selected permission and close the dialog. The list of configured permissions should now include the API.ReadWrite.All permission for the Business Central application. Click on Grant admin consent to pre-consent the permissions. This saves us an extra step and configuration when the application is created in Business Central.
The final step is to create a secret. The Client Credentials flow always requires a secret, it makes no difference between public or confidential applications. Click on Certificates & secrets in the left menu, then click on New client secret. In the dialog you can optionally set a description and change the expiration date. Then click on Add to create the secret.
Click on the copy button right behind the secret value and paste the secret somewhere (e.g. Notepad) for later usage.
The next step is to create an application account in Business Central for the Postman application. Open Business Central and search for Azure Active Directory Applications. Click on New to add a new record. Fill in the client id of the Postman app registration. This is the same client id that was used earlier in Postman and can be found on the Overview page of the Azure app registration. Give it a description and then add permissions. For test purposes, I usually go with user group D365 FULL ACCESS.
It’s not required to click on Grant Consent because we already granted the permissions in the Azure app registration. That’s why it’s called pre-consent.
Test service-to-service authentication with Postman
Let’s test this with Postman! Open Postman again and go to OAuth settings of the same request as before. We are going to make a few changes here. The settings should now be filled in as follows.
That’s it! Try to get a new access token and use it to make a new request to Business Central. I’ll save you another screenshot because it would look exactly the same as earlier.
Note that we still didn’t configure anything in the Business Central server instance. There is one setting called AppIdUri. According to the documentation, this setting is used “to validate the security tokens that the server instance receives in SOAP and OData calls”. However, I was able to call APIs without configuring this setting. If I would have configured this setting, then the value would be the Application ID URI as specified in the Azure app registration for the Business Central application: https://businesscentral.cronus.company.
Phew… that was a lot of configuring… And what do we have at the end of this exercise? Our own custom scopes to call Business Central on-premises with OAuth authentication. Well, because the app registration can be used across multiple Business Central server instances, at least we don’t have to do this for every single Business Central server instance.
But still… we have a custom scope… wouldn’t it be easier if we could use the standard scope for Business Central SaaS? That would be a lot easier, right?
Let’s move to option 2, the easy option!
Option 2: Standard scope
Instead of creating custom scopes, it’s also possible to configure Business Central to accept scopes that start with https://api.businesscentral.dynamics.com. And if we do that, then we can skip the configuration of the custom scopes we did in step 1!
The configuration setting is called ValidAudiences. This is related to a property in the access token, called aud. This property, officially called “claim”, identifies the intended recipient of the token. The audience is your app’s Application ID, assigned to your app in the Azure portal. With the configuration setting ValidAudiences we tell our Business Central server instance to accept tokens with an alternative audience claim. The configuration setting can contain multiple values, separated by semicolons. To set this property, run this PowerShell command followed by a restart of the server instance:
Set-NAVServerConfiguration -ServerInstance BC -KeyName ValidAudiences -KeyValue "https://api.businesscentral.dynamics.com" Restart-NAVServerInstance -ServerInstance BC
After this, you can call Business Central on-premises APIs with exactly the same scopes as used for the SaaS environment! How convenient is that! I guess you’d agree that it is much easier to work with one set of scopes for both SaaS environments and on-premises environments! And this also enables publishing apps from VS Code with Azure AD authentication. That would not be possible when using the custom scopes from the first option.
To test Postman with the user delegated flow (Authorization Code) follow the steps explained earlier, but change the scope to https://api.businesscentral.dynamics.com/user_impersonation.
To test Postman with service-to-service authentication (Client Credentials) you need to assign the standard Business Central permissions in the Azure app registration for Postman and then change the scope in Postman to https://api.businesscentral.dynamics.com/.default. See also the blogs series about service-to-service authentication for more information about configuring and testing service-to-service authentication.
There is one configuration setting that we didn’t touch yet, but it works automatically if you are using a single-tenant environment and exactly followed all steps in the previous blog post. This setting can be reviewed by calling the Get-NavTenant command:
The AadTenantId is filled with the Azure Active Directory ID. How did that happen, we didn’t run any command that configured this setting! This property is automatically set when you configure the WSFederationLoginEndpoint on the Business Central server instance. The URL starts with https://login.microsoftonline.com/[AAD Tenant Id]/wsfed and Business Central takes the Azure AD tenant id out of this URL and applies it to the Business Central tenant settings. That’s why I said in the previous blog post that this setting MUST be a guid and not the primary domain of the Azure Active Directory. If you would use the primary domain, then the Business Central tenant configuration is also set to the domain and then the service-to-service authentication will fail! I’ve just saved you a couple of hours and frustration…
What if you have a multi-tenant environment? Then you need to provide the AadTenantId with the Mount-NAVTenant PowerShell command. See https://docs.microsoft.com/en-us/powershell/module/microsoft.dynamics.nav.management/mount-navtenant for more information. This can’t be done dynamically, already mounted tenants need to be dismounted first and then mounted again with the AadTenantId parameter.
A final word about calling custom APIs with service-to-service authentication on on-premises environments. This is possible starting with version 19.5. In older versions, you will get an error message that you can’t access the object using application permissions. A workaround is to set the application user account to External user and then it will also work in older versions. That’s a workaround, not an official solution!