Blog

Build Simple License Manager API Services With JavaScript

by on February 22, 2019

API Service Simple Licenses Manager

Backendless gives you the capability to implement your own License Manager for creating and validating product licenses for your customers. Here, we examine how to put this into action using JavaScript and API services.

In this step-by-step guide, we will demonstrate how to use Backendless API services and features such as data management and Business Logic (Cloud Code) to create and manage product licenses.

Additionally, we will use the Backendless JavaScript SDK and JavaScript Code Generator to build a test application to try out our functions.

Setup Backendless

Login to your Backendless developer account. If you do not have one, you can register for free at develop.backendless.com.
Register For Backendless Account
Create a new Backendless application, or if you already have one, you can use your pre-existing app. The application we’ll use for this article will be called LicenseManager.

Database Schema

First, we should create a new Table in DataService. Let’s call it Licenses. Specify some columns as described in the following image:
Simple Licenses Manager Database Schema
As you can see, there are only two additional columns:

  • token {String}: a random string, and going forward, this is not actually the license token that we send to the client
  • expired {Date}: the date when the license becomes invalid

We should also set up Roles Permissions for the table to prevent any manipulation with the table records except ServerCode; see the next screenshot:
Simple Licenses Manager Data Management Permissions
You can check this out through the REST Console; just go there and try to execute CRUD methods. From the GET method, you will get an empty array of licenses even if there are a few records. From other methods, you will get exceptions.
Simple Licenses Manager REST Console
Simple Licenses Manager REST Console 2

License API Service

Our License Manager will work through an API Service. This gives us some security and the ability to write our custom logic as well. We’re going to create that service using JavaScript, which will be running in a NodeJS environment, and we will use some NodeJS modules for implementing our needs.

So let’s create a new API Service and call it LicenseManager. For that, you will go to Business Logic (Cloud Code) -> Coding and create a new JS file with the following content:

class LicenseManager {
 /**
  * @param {Number} expiredIn
  *
  * @returns {Object}
  */
 async create(expiredIn) {
 
}
 /**
  * @param {String} id
  */
 async remove(id) {
 }
 /**
  * @param {String} id
  * @param {String} token
  *
  * @returns {Boolean}
  */
 async verify(id, token) {
   
 }
}
Backendless.ServerCode.addService(LicenseManager)

Click the Save & Deploy button to deploy the service onto the server.
Simple Licenses Manager Server Code
Once it is deployed, you can go to the API Service section and in the left sidebar, you will see the service.
Simple Licenses Manager API Services
As you can see, now all of the methods have no logic. We will walk through each method of the service in more detail.

Create a New License

As we mentioned before, we will not give the generated license token to the client; we will generate a new, long license token string and the long token will be sent to the client. In the database, we will keep only the encrypted version of the token. NodeJS has its own “crypto” module. It will help us generate the long token and then encrypt the token. So let’s add a few functions for that to the top of our service.

const Crypto = require('crypto')
const SECRET = 'some secret string'
function generateToken(){
return Crypto.randomBytes(128).toString('hex')
}
function encryptToken(token, secret) {
return Crypto.createHmac('sha256', secret).update(token).digest('hex')
}

The SECRET variable contains a secret passphrase. A little bit later, we will move the value to ServiceConfiguration.

As you recall, we are going to keep our licenses in the Licenses table. Specify a dataStore for the table somewhere before the LicensesManager service class.

const LicensesStore = Backendless.Data.of('Licenses')

It looks like we have everything that we need to generate a new license; let’s add some logic to the create method.

/**
  * @param {Number} expiredIn
  *
  * @returns {Object}
  */
 async create(expiredIn) {
   const token = generateToken()
   const license = await LicensesStore.save({
     token : encryptToken(token, SECRET),
     expired: Date.now() + (expiredIn * ONE_DAY),
   })
   return {
     token,
     id: license.objectId
   }

As you can see, the method logic is pretty simple and doesn’t require any additional explanation, except for the ONE_DAY variable. In most cases, licenses generated last for a long period, months or days, so the create method expects only one argument: expiredIn (days). For example, if we want to generate a new license for a month, we just pass 30, for a week, just pass 7, and so on.

Just specify the constant at the top of the service’s file.

const ONE_DAY = 24 * 60 * 60 * 1000

Let’s see how our service looks now:

const Crypto = require('crypto')
const SECRET = 'some secret string'
const ONE_DAY = 24 * 60 * 60 * 1000
function generateToken(){
 return Crypto.randomBytes(128).toString('hex')
}
function encryptToken(token, secret) {
 return Crypto.createHmac('sha256', secret).update(token).digest('hex')
}
const LicensesStore = Backendless.Data.of('Licenses')
class LicenseManager {
 /**
  * @param {Number} expiredIn
  *
  * @returns {Object}
  */
 async create(expiredIn) {
   const token = generateToken()
   const license = await LicensesStore.save({
     token : encryptToken(token, SECRET),
     expired: Date.now() + (expiredIn * ONE_DAY),
   })
   return {
     token,
     id: license.objectId
   }
 }
 /**
  * @param {String} id
  */
 async remove(id) {
  
 }
 /**
  * @param {String} id
  * @param {String} token
  *
  * @returns {Boolean}
  */
 async verify(id, token) {
  
 }
}
Backendless.ServerCode.addService(LicenseManager)

Let’s check what we have. Just invoke the method from the Console and take a look at the returned result:

{
   "id": "E2CB0ABA-69EF-947D-FF5A-036EED260700",
   "token":   "28530d11319967b1a54d2cbf391e5a379445b282b30ca235f4bdba1fc791a7ba1847293e05c1d6c2b962dcd9ded1631cc52598ec0d337ed8842786da58e768c84fca75794d32a2d1a0c88e0c419abcc5fea5967caa847c7835a88d02de43a7ed72f67fefcc84d1e95129005402d901ddc032762078f1d21283983dc9dee06270"
}

Simple Licenses Manager API Services
And also take a look at the Licenses table:
Simple Licenses Manager Licenses Table
You probably noticed that the tokens are different. The service returns a longer result than it saved in the table. That’s because the service returns a long license token and then, before we save it in the table, we encrypt it.

Verify License

In the previous section, we completed a license generation, but we also need to have logic that will check the license. Replace the verify method with the following code:

/**
  * @param {String} id
  * @param {String} token
  *
  * @returns {Boolean}
  */
 async verify(id, token) {
   const query = Backendless.DataQueryBuilder
     .create()
     .setWhereClause(`objectId = '${id}' AND token = '${encryptToken(token, SECRET)}'`)
   const license = (await LicensesStore.find(query))[0]
   if (!license) {
     throw new Error('License does\'t exist')
   }
   if (license.expired < Date.now()) {
     throw new Error('License is expired.')
   }
   return true
}

As it was before, the method looks quite simple as well. Here’s what it’s doing:

The method is expecting two arguments, id and token. Then, we try to find a license object with the query objectId = ‘${id}’ AND token = ‘${encryptToken(token)}’. As you can see, before building the query, we transform the token with the encryptToken function as we did in the create method before saving the new license.

Then, if the license doesn’t exist, we just throw an exception. Otherwise, we check if the license isn’t expired yet. In the case that everything is OK, just return true.

Let’s check how it works. We’re going to use the licenseId and licenseToken that we got from the previous section:

{
   "id": "E2CB0ABA-69EF-947D-FF5A-036EED260700",
   "token":   "28530d11319967b1a54d2cbf391e5a379445b282b30ca235f4bdba1fc791a7ba1847293e05c1d6c2b962dcd9ded1631cc52598ec0d337ed8842786da58e768c84fca75794d32a2d1a0c88e0c419abcc5fea5967caa847c7835a88d02de43a7ed72f67fefcc84d1e95129005402d901ddc032762078f1d21283983dc9dee06270"
}

If you didn’t save that value, don’t worry, just create a new license and use the result for verification.
Simple Licenses Manager License Check Test
It works, now let’s change the id or token value and see what result will be returned.
Simple Licenses Manager License Check Fail
Ok, that works as expected as well, but what about license expiration? Let’s simulate a case where the license is already expired. For that, go to your Data Browser and change the expired value for the license:
Simple Licenses Manager Expiration Date Test
Now let’s go back to the API Service and try to verify the license: Simple Licenses Manager Verify License

Remove License

In some cases, you may want to remove a license by licenseId. Of course, you can always do that via the Data Browser, but let’s add a simple method for that.

/**
  * @param {String} id
  */
 async remove(id) {
   await LicensesStore.bulkDelete(`objectId = '${id}'`)
}
So, in the end our service should looks like this:
const Crypto = require('crypto')
const SECRET = 'some secret string'
const ONE_DAY = 24 * 60 * 60 * 1000
function generateToken(){
return Crypto.randomBytes(128).toString('hex')
}
function encryptToken(token, secret) {
return Crypto.createHmac('sha256', secret).update(token).digest('hex')
}
const LicensesStore = Backendless.Data.of('Licenses')
class LicenseManager {
/**
 * @param {Number} expiredIn
 *
 * @returns {Object}
 */
async create(expiredIn) {
  const token = generateToken()
  const license = await LicensesStore.save({
    token  : encryptToken(token, SECRET),
    expired: Date.now() + (expiredIn * ONE_DAY),
  })
  return {
    token,
    id: license.objectId
  }
}
/**
 * @param {String} id
 */
async remove(id) {
  await LicensesStore.bulkDelete(`objectId = '${id}'`)
}
/**
 * @param {String} id
 * @param {String} token
 *
 * @returns {Boolean}
 */
async verify(id, token) {
  const query = Backendless.DataQueryBuilder
    .create()
    .setWhereClause(`objectId = '${id}' AND token = '${encryptToken(token, SECRET)}'`)
  const license = (await LicensesStore.find(query))[0]
  if (!license) {
    throw new Error('License does\'t exist')
  }
  if (license.expired < Date.now()) {
    throw new Error('License is expired.')
  }
  return true
}
}
Backendless.ServerCode.addService(LicenseManager)

Service Configuration

As you recall, I promised you that we’d move the SECRET variable into the service configuration. It’s one of the most important features in our API Service. Through that, you can create reusable services or change your configuration without changing code. Just replace the last line where you register the LicenseManager service with the following:

Backendless.ServerCode.addService(LicenseManager, [
{
  order              : 0,
  displayName : 'Secret Key',
  name              : 'secretKey',
  type         : Backendless.ServerCode.ConfigItems.TYPES.STRING,
  defaultValue : 'AC0d140cae3f507552c54d2103001308f5',
  required     : true,
  hint         : 'Please specify your Secret Key'
}
]

Remove the SECRET variable and replace where it’s used in the create and verify methods with the value from config ‘this.config.secretKey’ and deploy it.
Simple Licenses Manager Secret Key
Now you can change the secret key value really easily: open service configuration, change the value and click the “Save” button. But be aware, when you change the secret key, all the existing licenses will be invalid.
Simple Licenses Manager Secret Key Configuration

Service Security

This is the most important step because we must deny creating new licenses from outside; otherwise, anyone would able to create a new license and the service becomes useless. A new license must be created based on some action, such as when we receive payment from a customer. Let’s create another service. It will be a billing service, but since this article is about licenses, it will simply be a dummy service that we will call FooBar service. Inside the service method, we will call our LicenseManager service:

class FooBar {
 /**
  * @returns {Object}
  */
 async createLicense() {
   console.log('here we should do some work before create a new license')
   return (await Backendless.Request.post(`http://api.backendless.com/{YOUR_APP_ID}/{SERVER_CODE_API_KEY}/services/LicenseManager/create`).set('Content-Type', 'application/json').send(JSON.stringify(10)))
 }
}

Simple Licenses Manager Billing Service
Ok, check this out: go to API Services and invoke the service.
Simple Licenses Manager Billing Service Test
It works! But one important thing remains: the LicensesManager.create method is still insecure because we can invoke it directly. Let’s fix that. We will deny access to the create method for everyone except ServerCode.
Simple Licenses Manager Access Limitation
Simple Licenses Manager Limit Access with Permissions
As you can see, now it’s impossible to invoke the service method directly, but it works from the FooBar service.
Simple Licenses Manager Access Denied

Remove Expired Licenses by Timer

When a license becomes expired, we don’t need to keep that license in the Licenses table, so let’s create a new timer for removing expired licenses each hour.
Simple Licenses Manager Create Timer
Replace the execute function with the following code:

 async execute(req){
   await LicensesStore.bulkDelete(`expired < ${Date.now()}`)
 }

Now deploy the changes:
Simple Licenses Manager Deploy Timer
That’s all, every expired license will be removed automatically.   

How to Check the License from the Client

Most likely, you will want to check if the current license is still valid. This is not hard to implement; we need just to call the verify method of our LicensesManager service. As you recall, we only deny invocation from the outside for the create method for security reasons, but the verify method is still available for invoking from outside. Backendless has the ability to generate code for services by doing what’s described in the next screenshot:
Simple Licenses Manager Client-Side Verification
It will generate a JS project with the code of the API Service. Open the html file in your browser and you will see something like this:
Simple Licenses Manager Verify License App
In the browser’s console, you can play with all the methods of the service. Let’s generate a new license, remember licenseId and licenseToken, and check the license from the client.

const license = {
   "id": "YOUR_LICENSE_ID",
   "token": "YOUR_LICENSE_TOKEN"
}
LicenseManager.verify(license.id, license.token).then(console.log, console.error)

As expected, it works.
Simple Licenses Manager Client Test Success

Conclusion

Today we have done a simple API service for managing licenses. It isn’t too difficult to implement and can be quite helpful in many cases. We also touched on Permissions, learned how to create Timers, and showed what Service Configurations are.

Thanks for reading, I hope you’ve enjoyed it and see you soon.

Leave a Reply