Dynamics 365 Business Central: data encryption changements for Isolated Storage

I suggested many times in the past to every partners I met and to all the attendees to my AL courses for Microsoft Italy that the way to store sensitive data in Dynamics 365 Business Central must be the Isolated Storage usage and not custom tricks on custom tables (see here) or using the old Service Password table (deprecated in Wave 2 release).

Encryption

In this days, during an extension’s code checking for a partner where I had previosuly suggested those things, I see a license control checking like the following:

  • They have a function for downloading a license key (call to an external service)
  • This license key is saved into the Isolated Storage with Datascope = Module (visible to all the extension
  • They have a codeunit with a subscriber to the OnAfterLogInEnd event with the following code:
[EventSubscriber(ObjectType::Codeunit, Codeunit::LogInManagement, 'OnAfterLogInEnd', '', false, false)]
local procedure CheckLicense()
var
   LicenseKeyValue: Text;
   validLicense: Boolean;
begin
   validLicense := false;
   if IsolatedStorage.Get('LicenseKey',DataScope::Module,LicenseKeyValue) then
   if not LicenseCheck(LicenseKeyValue) then
         error('Your extension license is not valid')
      else
         //Valid license found
         validLicense := true
   else
      if CheckLicensingLimit() then
         error('You don''t have a license for this extension.');
end;

local procedure LicenseCheck(LicenseKey: Text): Boolean
begin
   //custom license check here
end;

local procedure CheckLicensingLimit(): Boolean
begin
   //if extension's installation date + N days are overdue, the extension cannot be used again
   exit false;
end;

In this event subscriber, they search for a license key variable in the Isolated Storage. If the variable is found, a license check is performed (by calling the LicenseCheck procedure) and an error is thrown if the license is not valid.

At the same manner, an error is thrown also if the license key is not found in the Isolated Storage (the extension was never licensed by the user). For this task, they save in the Isolated Storage via an Install codeunit the date of the extension’s installation, then if this date + N limit days are overdue, an error is thrown (the customer has N days to acquire a license for this extension).

All is good as logic, but perform this license check in the OnAfterLogInEnd event and throwing an error here is absolutely to avoid. If your extension’s license check is failed (missing license or not valid license) you cannot login to that tenant anymore!

My suggestion: throw an error from features of your extension or disable your extension’s business logic, but don’t throw errors from standard events in the application. N extensions can be installed on a tenant (you’re not alone) and you can’t block others if your extension’s license is not valid.

Another important thing to remember is the changements in secret management from Dynamics 365 Business Central Wave 2 release (explained here) related to Isolated Storage:

  • In Dynamics 365 Business Central SaaS, sensitive data stored in the Isolated Storage are always encrypted.

 

  • In Dynamics 365 Business Central on-premise, encryption is controlled by the end user (via the Data Encryption Management page), and here:
    • If encryption is turned on, a secret stored in the Isolated Storage is automatically encrypted.
    • A secret that was inserted while encryption was turned off will remain unencrypted  if encryption is turned on.
    • If you turn off encryption, the secret will be decrypted.
       

     

Accordingly to these changes, if you have an extension that works for Dynamics 365 Business Central SaaS and on-premise (same code) and you’re using the Isolated Storage to store secrets, you need to check if the encryption is enabled (always true for SaaS) and then save the secret accordingly.

So, a function that saves a license key to the Isolated Storage and that works for Dynamics 365 Business Central SaaS and on-premise will be as follows:

local procedure SaveLicense()
var
   licenseKeyValue: Text;
begin
   if not EncryptionEnabled() then
      IsolatedStorage.Set('LicenseKey',licenseKeyValue,DataScope::Module)
   else
      IsolatedStorage.SetEncrypted('LicenseKey',licenseKeyValue,DataScope::Module)
end;

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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