Blog

How to Allow API Calls to Data Service from Cloud Code Only

by on June 18, 2019

Restrict API Calls to Cloud Code Only

Today we’re going to take another look at data security configurations in Backendless. In this article, we will talk about how to restrict direct access to your data via API and only expose your custom API endpoints.

This does not mean you should use some other set of “admin” APIs for data management. Instead, it is accomplished by setting up proper security settings. Backendless provides ways to set up really granular permissions for your resources, including even row-level security in your data tables. But for this task, we’re only going to need system roles and global permissions. Thus, it won’t require you to do a lot of configurations or create additional assets, such as custom roles.

System Roles

Let’s first examine how the system roles are assigned to your app’s API users.

There are 6 “device” roles available in Backendless:

  • AndroidUser
  • DotNetUser
  • IOSUser
  • JSUser
  • RestUser
  • ServerCodeUser

There is also an ASUser role you can see in Backendless Console, but it is currently deprecated — it was used with the ActionScript SDK in version 3.

Each of these roles corresponds to a specific API key you use when the request is made. So, technically, there is no hard linking to a device, and if you use a JS API key in your Android project — then the calls will be considered as originating from a JavaScript “device”. Regarding our topic, the call will be assigned a role “JSUser” (among others). Similarly, when the request is made from your Business Logic code (now known as Cloud Code: custom API services, handlers or timers), then the role will be “ServerCodeUser” instead, since Cloud Code automatically uses the ServerCode API key (you don’t need to set it explicitly).

There are also two roles indicating whether a request originated from a logged-in user of your app:

  • AuthenticatedUser
  • NotAuthenticatedUser

This one is pretty simple: when there is a logged-in user, the role assigned is AuthenticatedUser; otherwise, it’s NotAuthenticatedUser.

There are also extra roles for the users logged in via social networks:

  • SocialUser
  • GoogleUser
  • FacebookUser
  • TwitterUser

The SocialUser role is assigned for every social user; additionally, the Google/Facebook/Twitter role is assigned depending on the specific social network the user used to log in.

To sum up with an example, if your application’s user has sent a login request to Backendless via Facebook from the iOS app, then he/she will have the following roles assigned:

  • AuthenticatedUser
  • IOSUser
  • SocialUser
  • FacebookUser

Global Permissions

Now, to control access to your assets globally, you use global permissions. The settings are available in Backendless Console at the Users tab -> Security Roles. There you can allow or deny specific operations for specific service.

Returning to our initial topic, we’d like to allow our end-users’ apps to talk exclusively to our Custom Services API and never allow direct access to the data. This actually is a pretty typical setup for many web apps: if you write the backend yourself, you rarely allow the client app to access your underlying database directly. Instead, you provide API methods that do the low-level job. With Backendless, this is not what we have out-of-the-box since security constraints like this will make your first acquaintance with the service much harder; you’ll have to define some custom methods to test, say, the retrieval of the Users list. But in the end, this is what you likely should aim for.

Data Security in Practice

An obvious solution to constrain the data assets from direct access is to only allow the ServerCodeUser role to access them and deny the operations for both AuthenticatedUser and NotAuthenticatedUser (this will effectively apply to all users). But since the system roles have equal priority, and Backendless’ security policy is “if any of the roles denies the operation, then the operation will be denied”, the call from Cloud Code still won’t succeed because the request role (which is either AuthenticatedUser or NotAuthenticatedUser) will be propagated further.

To solve this specific problem, there is an exception documented for the roles assignments when the API call originates from Business Logic/Cloud Code:

Business logic is the only exception to the rule for assigning NotAuthenticatedUser and AuthenticatedUser roles. When business logic makes an API call and there is no authenticated user in the context of the call, Backendless assigns only the ServerCodeUser role. Otherwise, if there is an authenticated user, then both ServerCodeUser and AuthenticatedUser roles are assigned to the request.

Therefore, in the aforementioned case in the request from Cloud Code, the actual effective role will be just ServerCodeUser, omitting the NotAuthenticatedUser role. As a result, only permissions which are set to ServerCodeUser will apply, and the request will succeed. At the same time, if that user tries to access data assets directly, the NotAuthenticatedUser role won’t be omitted and the request will fail due to a permissions error.

Unfortunately, the same logic does not apply in the event that the user did log in and hence has the AuthenticatedUser role. You could work around it by logging out inside of your Cloud Code, but that would log out the user who makes a request, which is totally undesired. Instead, you have to go a bit lower level. The “logged in” state relies on whether the user-token is sent along with the request. This said, depending on the SDK you use, you can hack it to not send the user-token from the specific call in Cloud Code and thus the flow will become the same as in the case described above. And if you use REST API to make calls from Cloud Code, it’s even simpler — just do not send the user-token header.

This is just one example of how to apply strict security to your backend in Backendless. We hope you will appreciate how easy it can be compared to more complex settings in the proxy (e.g. Nginx) or the database (e.g. MySQL) when the backend is developed by yourself.

Thanks for reading!

Leave a Reply