Control of access in apps using basic connection

Introduction

If your app uses a basic connection, you can easily control access to your app by implementing access control and transforming the connection from basic to oauth type of connection. The transformation offers several advantages, including:

  • restricting app usage to only those users with granted access

  • revoking access when necessary, such as in cases of expired subscriptions to your app

  • maintaining the authorization flow that establishes a new connection with provided credentials to the API service

  • ensuring that the access control flow is secure and not exposed in the browser console or DevTool

  • centralizing access control code within a single location, preventing redundancy across modules or RPCs

  • managing a reasonable volume of requests to the backend responsible for access control within your app

In this article, you will learn how to easily control access to your app by transforming a basic connection to an oauth connection.

The solution described below is just one of many possible approaches. Feel free to explore alternative methods as well.

Database of users with granted access to your app

First, you need a database with the list of users with granted access, their credentials, timezone, and the date of access expiration.

In our guide, we store the list of users and their credentials in Google Sheets spreadsheet. The spreadsheet contains the following columns:

  • EMAIL (text): Email of the user for better recognition.

  • TOKEN (text): Token that was provided to the user to access the app.

  • VALID UNTIL (date-time): The date until which the user can access the app.

You can easily automate the subscription flow with a scenario in Make, for example:

  • the generation of a token for a new user

  • updating the date of expiration upon the user's payment for the month/year subscription

Backend for the access control flow

To access the database of users, you need to implement an endpoint, that whenever called will return information on whether the user has been granted access to your API. In our guide, we use a scenario in Make, that handles the backend of the access control.

You can create the scenario from the template. You can skip the guided setup in the template.

Detailed description of the scenario used as the backend for the access control

  1. Webhooks > Custom webhook: This module listens to a request, that contains data with user's credentials which you have provided to the user. In our case, we used the parameter token.

  2. Google Sheets > Search Rows: This module searches for the record belonging to the user. In our guide, we look up via the token parameter.

  1. Router: The router handles 2 available results - the user does/doesn't have access to the app.

Notice that the function works with the timezone parameter. The timezone of the developer is used.

The timezone parameter is used because the mapped date-time is not a timestamp and does not include information about the timezone.

Do not forget to edit the timezone value according to the timezone your system is working in.

  1. Webhooks > Webhook Response (1): If the user does have access to the app, status code 200 is returned to the webhook together with the date of expiration.

Notice that the function works with the timezone parameter. The timezone of the developer is used.

The timezone parameter is used because the mapped date-time is not a timestamp and does not include information about the timezone.

This ensures that the date of expiration is correctly parsed and the connection will expire in the user's timezone, for example 22.9.2023 23:59:59 in America/Chicago.

Do not forget to edit the timezone value according to the timezone your system is working in.

  1. Webhooks > Webhook Response (2): If the user doesn't have access to the app, status code 400 is returned to the webhook. Additionally, the error message is returned.

Notice, that we differentiate 2 situations:

  • the user doesn't have the correct credentials

  • the user does have the correct credentials but their subscription has expired

Connection implementation in the app

Now, since we have the backend for access control ready, we can use it in our app.

It is recommended to first implement the basic connection and make sure it works correctly.

1. The current implementation of basic connection

In our guide, the API we connect our app to uses basic access authentication. Therefore, this code in the communication tab in basic connection was originally implemented:

Go to your basic connection, that you have implemented and tested, and have it open in your web browser.

{
	"url": "https://api.example.cz/v2/whoami",
	"headers": {
		"authorization": "Basic {{base64(parameters.username + ':' + parameters.password)}}"
	},
	"response": {
		"valid": {
			"condition": "{{body.status != 'error'}}"
		},
		"error": {
			"200": {
				"message": "[{{body.status}}]: {{ifempty(body.message.items, body.message)}}"
			},
			"message": "[{{statusCode}}]: {{body.reason || body.message}}"
		},
		"metadata": {
			"type": "text",
			"value": "{{body.username}}"
		}
	}
}

2. Creation of a new oauth connection

In your web browser, open a new tab with your app, go to the tab Connections and click on Create a new Connection. In the pop-up window, select the type Oauth 2 (authorization code). Click Save.

3. Setting up connection parameters

Go to the tab with the basic connection and copy the connection parameters to your clipboard. Then, go to the second tab with your new oauth connection and paste the connection parameters from your clipboard. Then, enter the parameters you use for your access control. Save the changes.

In our guide, we used our token parameter.

Notice the token parameter that doesn't belong to the integrated API.

The token parameter is used by us to control the app access.

4. Setting up the access control flow in connection

Implementation of authentication to the API

Go to the tab with the basic connection and copy the code in communication tab. Then go to the second tab with your new oauth connection and paste the code into the info directive. Nothing needs to be changed in the code.

Implementation of access control to our app

Now, you need to implement the token directive, that will call the backend for access control. The token directive should contain:

  • URL: Url of the endpoint that handles the access control, in our guide we used our webhook.

  • User's credentials parameters: The parameters that you share with the user are passed to the endpoint handling access control. In our guide, we used token parameter.

  • Error handling: The directive that handles the error returned from the endpoint. Also, it returns the error message from the response.

  • Access control flow: The flow, that ensures the connection is created only if the user has been granted access to the app, or re-verified when the user's connection expires. The flow works with these parameters:

    • condition - A condition that makes sure the token directive is also executed whenever the existing connection expires. When a module with an expired connection is triggered in a scenario, the token directive is executed.

    • response.data.expires - A date-time parameter that says when the connection will expire. This ensures that once the connection expires, the token directive with the endpoint for checking whether the user still has a valid subscription to your app will be called, and the date of expiration extended.

After you implement the code in the communication tab, save the changes.

Connection's communication code with comments explaining the functionality of each parameter:

{
  // Token directive is used for access control flow.
  "token": {
	  // The condition ensures that the token directive is not only executed when
	  // connection is being created, but also when the connection is going to
	  // expire in 1 minute.
	  // The call in the token directive is executed when a module with expired
	  // connection is triggered.
	  "condition": "{{if(data.expires, data.expires < addMinutes(now, 1), true)}}",
	  // URL to the webhook in the scenario handling access control backend.
	  // Passing the user's token to the webhook.
	  "url": "https://hook.eu1.make.com/webhookID?token={{parameters.token}}",
	  "response": {
		  "error": {
			  // Mapping the error message from the webhook's response.
			  "message": "[{{statusCode}}]: {{body.error}}"
		  },
		  "data": {
			  // Mapping the date-time of expiration from the webhook's response.
			  "expires": "{{body.expires}}"
		  }
	  }
  },
  // Info directive is used for validation of the user's credentials in the API service.
  // Nothing needs to be changed in the original code.
  "info": {
	  "url": "https://api.example.cz/v2/whoami",
	  "headers": {
		  "authorization": "Basic {{base64(parameters.username + ':' + parameters.password)}}"
	  },
	  "response": {
		  "valid": {
			  "condition": "{{body.status != 'error'}}"
		  },
		  "error": {
			  "200": {
				  "message": "[{{body.status}}]: {{ifempty(body.message.items, body.message)}}"
			  },
			  "message": "[{{statusCode}}]: {{body.reason || body.message}}"
		  },
		  "metadata": {
			  "type": "text",
			  "value": "{{body.username}}"
		  }
	  }
  }
}

Notice that we didn't use sanitization, therefore, the connection logs will not be available. The webhook's URL will not be exposed.

5. Connecting the new oauth connection to a module

In order to test the right functionality of the oauth connection, you need to connect it to an existing module. Go to a module and remove the current basic connection, then, connect the new oauth connection.

6. Testing the correct functionality of access control in the app

Since the new oauth connection is created and connected to a module, you can test its correct functionality, and adjust it if needed. The test cases in our example are described below.

To ensure the proper evaluation of access control functionality within your application, you will need to periodically modify the value in the VALID UNTIL column in the table of users multiple times.

For example, now is 22.9.2023 15:43:00and you need to verify, that the verification process will work correctly, therefore, you can set the date 22.9.2023 15:45:59 so that you have enough time to create a connection and then run the module after the expiration.

Testing of the creation of a connection

  • Test case: An invalid token has been provided.

  • Expected result: A connection is not created due to an error from the access control endpoint.

Notice the error message that was returned from the access control endpoint.

Testing of the expiration of the connection during the app's use

The goal the access control is to be able to manage the expiration of the access to the app. Below, you can see how it will behave when the connection expires and the access to the app hasn't been prolonged. The user is not able to use the module until they pay for their subscription.

7. Switching the connection from basic to oauth in all modules, webhooks, RPCs

If you already have created modules, webhooks, and/or RPCs, once you confirm that your oauth connection with access control works as expected, you will need to switch the connection from the original one (basic) to the new one (oauth) in all modules, webhooks and RPCs.

If you just created the app and managed to make the connection work, just connect the new (oauth) connection to the new module/webhook/RPC whenever you create it.

If your app is still private, you can delete the old (basic) connection.

If your app is already public, it is recommended to rename the old connection (basic) so it is obvious it should not be used anymore.

Example of a new connection label: [DO NOT USE] Make

Conclusion

In the article, you learned how to easily control access to your app by implementing access control with a scenario and transforming a basic connection to oauth connection.

Last updated