A question I get quite often during my AL development workshops or coaching sessions for partners is this: can other apps access data that is stored in my app?
And the short answer is: yes, they can. There is no way to avoid that.
Let’s not go into the discussion why partners want to prevent others from peeking into data stored in their tables and or to see there table structure. Every partner has their reasons for that and it’s an endless debate between pro’s and con’s.
But let’s be clear about one thing: the data in the database belongs to the customer, not to the partner. It’s their data and nobody should prevent the customer from getting access to their data. Well… that’s my opinion at least… Anyway, there might be situations where you still want to protect data. Let’s look at a few examples.
Storing license data
AppSource doesn’t provide any way to license or monetize your app. So partners are left with two options: either give away their app for free (not an option many would say…) or to build in a license model. Which means they have to store license data somewhere. I think I don’t have to explain potential problems when customers can get their hands on that data and tamper with it. So the question is very valid here: how to store data out of sight of anybody else.
Storing access keys
Another example is when your app has an integration with external servers, e.g. a web service. You don’t want to store any access keys or passwords in tables that can easily be accessed by others.
These two examples can be easily be extended with others. But where it comes down to is the question: how to store sensitive and confidential information.
This is where the new feature of Business Central comes in: Isolated Storage. This feature is briefly described here.
Let’s first make clear what Isolated Storage is not: it’s not a property on a table that indicates that a table is only to be accessed by the app that creates the table. It is not even close to a table at all.
So, what is Isolated Storage? It provides a way to store key / value pairs in the database that can only be accessed by the app that stores that data. The value is simply any text value, not limited to 250 characters. It is stored as a BLOB in a new non-company table called ‘Isolated Storage’.
Data in the Isolated Storage has a scope. The scope determines who has access to the data. By default the data is accessible to all users who run the app. But you can also limit it to a particular user or to a single company. The access level is set with a new datatype, called DataScope.
When you store a value into the Isolated Storage, then you have to indicate the scope. The scope determines who will be able to read the data back from the Isolated Storage.
- Module – Globally in the whole app, across all companies and users
- Company – By all users but limited to the company that is used to store the data
- CompanyAndUser – Only in the company and only by the user who stored the data
- User – Only by the user who stored the data, but across all companies
Isolated Storage API
Now that we have seen that we can actually store data with different access levels, we need to look at how to store and read the data.
I guess the functions speak for itself…
All functions need a key to identify the value you want to store or get. This key is a text and can be anything you choose. The maximum lenght of the key is 200 characters. You can use a GUID or just a readable key or anything that makes sense to you.
All function accept the DataScope as an optional parameter. If you omit DataScope, then the value DataScope::Module will be used.
The data you store is a text value. This could be a simple string or a more complex piece of text like XML or JSON. It is highly recommended to encrypt the value before storing it, which will add another level of security.
Here is an example of how to use IsolatedStorage. This example exports the records of a table called License to JSON, then encrypts the Json value and finally stores the encrypted text in the Isolated Storage.
The function GetStorageKey just returns a GUID:
And with this function the value is read back into the table.
Do not create a codeunit with public functions that expose the sensitive data you store in the Isolated Storage. If you do that, then the Isolated Storage becomes useless, because one could easily create a dependent app on your app, call that function and get his hands on the sensitive data. Only data should be exposed that is safe to be exposed. So think twice about your code that handles the Isolated Storage.
In examples above, the table is declared as a temporary table in a Codeunit. The data never leaves the Codeunit and the Codeunit has only functions to tell if a valid license exists. With that approach, the data is completely isolated and nobody could tamper with it.
I would be even more happy if it was possible to have really isolated tables instead of only storing text values. However, I do understand that it would require a way more complex modification to the platform because it affects features like RecordRef or the option to run a table in the web client. And since isolated storage will probably only be used for specific circumstances, like storing licenses or other confidential data I can certainly see this feature as a solution that fit our needs.
It would be nice to get a generic function to export and import table to JSON though… Now I had to create a function myself to read and write every single field to a JSON value.
Enjoy this new feature and don’t forget to tell Microsoft if you have any improvement for this feature!