Tuesday, September 13, 2022

OAuth Bearer Token in Business Central and Azure

##Problem Description


The old Microsoft implementation of Web Services authorization is a giant security hole. Anyone who knows the current NON-expiring user id and the access key should be able to read and tamper with the accounting data. Moreover, anyone with sufficient access in user card could generate a new access key.


Microsoft has deprecated the feature and in future releases will not make it available to the Cloud installations of Business Central. (On-premise installation could still use it though for the foreseeable future.)
The OAuth token has a limited lifetime and needs to be constantly refreshed. We could automatically refresh the token - forever. (Only generating the first token requires Azure active directory login.)
(Should we go to prod before Microsoft totally removes the current Web service Access Key method, we need to wipe out any existing web access keys used by the consultants or internal users.)

##Register Azure app as described here in detail:

https:/
/www.youtube.com/watch?v=lCzqg2N0vbA
(I have downloaded the above Youtube video locally in the remote case the author pulls out the video. Microsoft's documentation is absurdly insufficient in implementing the plumbing - in this article they the general road map is listed:

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/authenticate-web-services-using-oauth

)

The already-registered app is shown below. Note that you don't need to ever create the actual app. The registration is sufficient - the registration is a proxy to get the access and refresh tokens.

https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade

**In Prod you may have to either alter the registered app's parameters to point to the Prod URLs or create a new registered app with the new Prod URLs.**

##Get the first token from PostMan:
Set the parameters and click on "Get New Access Token". (I could provide you the Postman parameters on Teams. Basically they are derived from the registered app.)
Active Directory login will pop up and you need to supply your credentials.

![image.png](.attachments/image-1daafd73-4f7a-474f-bb9b-71038ccff1e6.png)

Copy and paste the access token. and then scroll down and copy and paste the refresh token.

You will put the tokens into the following SQL. But before executing the SQL, stop the instance of the Prefix OAuth Token refresher scheduled task shown below.
_update [NavisionIntegration].[dbo].[PrefixBusinessCentralOAuthToken] set [access_token]=‘YourNewAccessToken’,
[refresh_token]=‘yournewRefreshToken’_

![image.png](.attachments/image-150975ad-0725-46f3-b0de-d6e1e08c5bb9.png)
##Stop instances of PrefixBusinessCentralOAtuhTokenRefresher scheduled task
PrefixBusinessCentralOAtuhTokenRefresher project under the Prefix.Automation solution is a scheduled task that uses the refresh_token to get a new access token.
Stop dev-apps and Fenix instances of scheduled task
![image.png](.attachments/image-5c8fb5b0-e352-4775-8ded-9d8335791bdb.png)
##Store the access and refresh token in DB

update [NavisionIntegration].[dbo].[PrefixBusinessCentralOAuthToken] set [access_token]='YourNewAccessToken', [refresh_token]='yournewRefreshToken'
##Run in your server machine

**Note: run only one instance of the refresher app. Since there is now only one registered app in Azure. Otherwise you would issue multiple unnecessary refreshes per hour**

**Note: Most of the time the refresher scheduled task is disabled in dev-apps. The is to save some bandwidth against Azure. That means the token, and more importantly, the refresh token in the database would be obsolete since both would have expired. In that case you need to get a newly seeded access and refresh tokens from Postman.**
###PrefixOAuthMicroService
All apps get the latest token from the database through a call to **PrefixOAuthMicroService** project which is also in the Prefix.Automation solution.
private static string GetOAuthAccessToken()
{
var client = new HttpClient();
Task resultOrder = client.GetStringAsync(ConfigurationManager.AppSettings["PrefixOAuthMicroService"]);

return resultOrder.Result;
// or get it from the database:
/* NavisionIntegrationEntities model = new NavisionIntegrationEntities();
var lst = model.usp_GetPrefixBusinessCentralOAuthToken().ToList();
return lst[0].access_token;*/
}

where in config the micoservice URL is specified:

Since the token expires every hour or so by Microsoft, we have to get the common token from DB, and NOT form config file(s), and refresh it 24/7 .
(Extending the expiration period of the access token was not possible with an Azure cmdlet. Also the refresh token has a much longer expiration period than the access token. However since we are constantly refreshing with
the PrefixBusinessCentralOAtuhTokenRefresher
task, our taken would be up-to-date indefinitely. I've tested this for days running though automation. )

Should go down for hours in production, we may have to re-generate the original access and refresh tokens and store them in the NavisionIntegration database before starting the scheduled task.


No comments: