# Rule-Based Access to Atlas Data API

MongoDB Atlas App Services have extensive serverless backend capabilities, such as Atlas Data API, that simply provide an endpoint for the read and write access in a specific cluster that you can customize access to later. You can enable authentication by using one of the authentication providers that are available in Atlas App Services. And, customize the data access on the collections, based on the rules that you can define with the App Services Rules.

In this blog post, I’ll walk you through how you can expose the data of a collection through Atlas Data API to three users that are in three different groups with different permissions.

## Scenario

* Dataset: We have a simple dataset that includes movies, and we will expose the movie data through Atlas Data API.
* We have three users that are in three different groups.
    * Group 01 has access to all the fields on the **movie** collection in the **sample_mflix** database and all the movies available in the collection.
    * Group 02 has access only to the fields **title, fullplot, plot,** and **year** on the **movie** collection in the **sample_mflix** database and to all the movies available in the collection.
    * Group 03 has access only to the fields **title, fullplot, plot,** and **year** on the **movie** collection in the **sample_mflix** database and to the movies where the **year** field is greater than **2000**.

Three users given in the scenario above will have the same HTTPS request, but they will receive a different result set based on the rules that are defined in App Services Rules.
## Prerequisites
* Provision an Atlas cluster (even the tier M0 should be enough for the feature to be tested).
* After you’ve provisioned the cluster, load the sample data set by following the steps.

## Steps to set up
Here's how you can get started!
### Step 1: Create an App Services Application
After you’ve created a cluster and loaded the sample dataset, you can create an application in App Services. Follow the steps to create a new App Services Application if you haven’t done so already.

I used the name “APITestApplication” and chose the cluster “APITestCluster” that I’ve already loaded the sample dataset into. 

### Step 2: Enable Atlas Data API
After you’ve created the App Services application, navigate to the **HTTPS Endpoints** on the left side menu and click the **Data API** tab, as shown below.

Hit the button **Enable the Data API**.

After that, you will see that Data API has been enabled. Scroll down on the page and find the **User Settings**. Enable **Create User Upon Authentication**. **Save** it and then **Deploy** it. 

Now, your API endpoint is ready and accessible. But if you test it, you will get the following authentication error, since no authentication provider has been enabled.

```bash
curl --location --request POST 'https://ap-south-1.aws.data.mongodb-api.com/app/apitestapplication-ckecj/endpoint/data/v1/action/find' \
> --header 'Content-Type: application/json' \
> --data-raw '{
>   "dataSource": "mongodb-atlas",
>   "database": "sample_mflix",
>   "collection": "movies",
>   "limit": 5
> }'
{"error":"no authentication methods were specified","error_code":"InvalidParameter","link":"https://realm.mongodb.com/groups/5ca48430014b76f34448bbcf/apps/63a8bb695e56d7c41ab77da6/logs?co_id=63a8be8c0b3a0268511a7525"}
```

### Step 3.1: Enable JWT-based authentication
Navigate to the homepage of the App Services application. Click **Authentication** on the left-hand side menu and click the **EDIT** button of the row where the provider is **Custom JWT Authentication**.

JWT (JSON Web Token) provides a token-based authentication where a token is generated by the client based on an agreed secret and cryptography algorithm. After the client transmits the token, the server validates the token with the agreed secret and cryptography algorithm and then processes client requests if the token is valid. 

In the configuration options of the Custom JWT Authentication, fill out the options with the following:

* Enable the Authentication Provider (**Provider Enabled** must be turned on).
* Keep the verification method as is (**Manually specify signing keys**).
* Keep the signing algorithm as is (**HS256**).
* Add a new signing key.
    * Provide the signing key name.
        * For example, **APITestJWTSigningKEY**.
    * Provide the secure key content (between 32 and 512 characters) and note it somewhere secure.
        * For example, **FipTEgYJ6WfUEhCJq3e@pm8-TkE9*UZN**.
* Add two fields in the metadata fields.
    * The path should be **metadata.group** and the corresponding field should be **group**.
    * The path should be **metadata.name** and the corresponding field should be **name**.
* Keep the audience field as is (empty).

Below, you can find how the JWT Authentication Provider form has been filled accordingly.

**Save** it and then **Deploy** it.

After it’s deployed, you can see the secret that has been created in the App Services Values, that can be accessible on the left side menu by clicking **Values**.

### Step 3.2: Test JWT authentication
Now, we need an encoded JWT to pass it to App Services Data API to authenticate and consequently access the underlying data. 

You can have a separate external authentication service that can provide a signed JWT that you can use in App Services Authentication. However, for the sake of simplicity, we’ll generate our own fake JWTs through jwt.io. 

These are the steps to generate an encoded JWT:

* Visit jwt.io.
* On the right-hand side in the section **Decoded**, we can fill out the values. On the left-hand side, the corresponding **Encoded** JWT will be generated.
* In the **Decoded** section:
    * Keep the **Header** section same. 
    * In the **Payload** section, set the following fields:
        * Sub. 
            * Represents owner of the token.
            * Provide value unique to the user.
        * Metadata. 
            * Represents metadata information regarding this token and can be used for further processing in App Services.
            * We have two sub fields here.
                * Name. 
                    * Represents the username of the client that will initiate the API request.
                    * This information will be used as the username in App Services.
                * Group.
                    * Represents the group information of the client that we’ll use later for rule-based access.
        * Exp.
            * Represents when the token is going to expire.
            * Provide a future time to keep expiration impossible during our tests.
        * Aud.
            * Represents the name of the App Services Application that you can get from the homepage of your application in App Services.
    * In the **Verify Signature** section:
        * Provide the same secret that you’ve already provided while enabling Custom JWT Authentication in the Step 3.1.

Below, you can find how the values have been filled out in the **Decoded** section and the corresponding **Encoded** JWT that has been generated. 

Copy the generated **JWT** from the **Encoded** section and pass it to the header section of the HTTP request, as shown below.

```bash
curl --location --request POST 'https://ap-south-1.aws.data.mongodb-api.com/app/apitestapplication-ckecj/endpoint/data/v1/action/find' --header 'jwtTokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMDEiLCJtZXRhZGF0YSI6eyJuYW1lIjoidXNlcjAxIiwiZ3JvdXAiOiJncm91cDAxIn0sImV4cCI6MTg5NjIzOTAyMiwiYXVkIjoiYXBpdGVzdGFwcGxpY2F0aW9uLWNrZWNqIn0.cq5Dr5fJ-BD1mBJia697oWVg_yWPua_NT5roUlxihYE' --header 'Content-Type: application/json' --data-raw '{
  "dataSource": "mongodb-atlas",
  "database": "sample_mflix",
  "collection": "movies",
  "limit": 5
}'
"Failed to find documents: FunctionError: no rule exists for namespace 'sample_mflix.movies"
```

We get the following error: “**no rule exists for namespace**.” Basically, we were able to authenticate to the application. However, since there were no App Services Rules defined, we were not able to access any data. 

Even though the request is not successful due to the no rule definition, you can check out the App Users page to list authenticated users as shown below. **user01** was the name of the user that was provided in the **metadata.name** field of the JWT.

### Step 4.1: Create a Role in App Services Rules
So far, we have enabled Atlas Data API and Custom JWT Authentication, and we were able to authenticate with the username **user01** who is in the group **group01**. These two metadata information (user and group) were filled in the **metadata** field of the JWT. Remember the payload of the JWT:

```json
{
   "sub": "001",
   "metadata": {
      "name": "user01",
      "group": "group01"
   },
   "exp": 1896239022,
   "aud" : "apitestapplication-ckecj"
}
```
Now, based on the **metadata.group** field value, we will show filtered or unfiltered movie data. 

Let’s remember the rules that we described in the Scenario:

* We have three users that are in three different groups.
    * Group 01 has access to all the fields on the **movie** collection in the **sample_mflix** database and all the movies available in the collection.
    * Group 02 has access only to the fields **title**, **fullplot**, **plot**, and **year** on the **movie** collection in the **sample_mflix** database and to all the movies available in the collection.
    * Group 03 has access only to the fields **title**, **fullplot**, **plot**, and **year** on the **movie** collection in the **sample_mflix** database and to the movies where the **year** field is greater than **2000**.

Let’s create a role that will have access to all of the fields. This role will be for the users that are in Group 01. 

* Navigate the **Rules** section on the left-hand side of the menu in App Services.
* Choose the collection **sample_mflix.movies** on the left side of the menu.
* Click **Skip** (**Start from Scratch**) on the right side of the menu, as shown below.

**Role name**: Give it a proper role name. We will use **fullReadAccess** as the name for this role.
**Apply when**: Evaluation criteria of this role. In other words, it represents when this role is evaluated. Provide the condition accordingly. **%%user.data.group** matches the **metadata.group** information that is represented in JWT. We’ve configured this mapping in Step 3.1.
**Document Permissions**: Allowed activities for this role.
**Field Permissions**: Allowed fields to be read/write for this role.

You can see below how it was filled out accordingly. 

After you’ve saved and deployed it, we can test the curl command again, as shown below:

```
curl --location --request POST 'https://ap-south-1.aws.data.mongodb-api.com/app/apitestapplication-ckecj/endpoint/data/v1/action/find' \
> --header 'jwtTokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMDEiLCJtZXRhZGF0YSI6eyJuYW1lIjoidXNlcjAxIiwiZ3JvdXAiOiJncm91cDAxIn0sImV4cCI6MTg5NjIzOTAyMiwiYXVkIjoiYXBpdGVzdGFwcGxpY2F0aW9uLWNrZWNqIn0.cq5Dr5fJ-BD1mBJia697oWVg_yWPua_NT5roUlxihYE' \
> --header 'Content-Type: application/json' \
> --data-raw '{
>   "dataSource": "mongodb-atlas",
>   "database": "sample_mflix",
>   "collection": "movies",
>   "limit": 5
> }' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  6192    0  6087  100   105   8072    139 --:--:-- --:--:-- --:--:--  8245
{
    "documents": 
        {
            "_id": "573a1390f29313caabcd4135",
            "plot": "Three men hammer on an anvil and pass a bottle of beer around.",
            "genres": [
                "Short"
            ],
            "runtime": 1,
            "cast": [
                "Charles Kayser",
                "John Ott"
            ],
            "num_mflix_comments": 0,
```

Now the execution of the HTTPS request is successful. It returns five records with all the available fields in the documents.

### Step 4.2: Create another role in App Services Rules

Now we’ll add another role that only has access to four fields (**title**, **fullplot**, **plot**, and **year**) on the collection **sample_mflix.movies**.

It is similar to what we’ve created in [Step 4.1, but now we’ve defined which fields are accessible to this role, as shown below.

**Save** it and **Deploy** it.

Create another JWT for the user **user02** that is in **group02**, as shown below.

Pass the generated Encoded JWT to the curl command:

```
curl --location --request POST 'https://ap-south-1.aws.data.mongodb-api.com/app/apitestapplication-ckecj/endpoint/data/v1/action/find' \
> --header 'jwtTokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMDIiLCJtZXRhZGF0YSI6eyJuYW1lIjoidXNlcjAyIiwiZ3JvdXAiOiJncm91cDAyIn0sImV4cCI6MTg5NjIzOTAyMiwiYXVkIjoiYXBpdGVzdGFwcGxpY2F0aW9uLWNrZWNqIn0.llfSR9rLSoSTb3LGwENcgYvKeIu3XZugYbHIbqI29nk' \
> --header 'Content-Type: application/json' \
> --data-raw '{
>   "dataSource": "mongodb-atlas",
>   "database": "sample_mflix",
>   "collection": "movies",
>   "limit": 5
> }' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3022    0  2917  100   105   3363    121 --:--:-- --:--:-- --:--:--  3501
{
    "documents": 
        {
            "_id": "573a1390f29313caabcd4135",
            "plot": "Three men hammer on an anvil and pass a bottle of beer around.",
            "title": "Blacksmith Scene",
            "fullplot": "A stationary camera looks at a large anvil with a blacksmith behind it and one on either side. The smith in the middle draws a heated metal rod from the fire, places it on the anvil, and all three begin a rhythmic hammering. After several blows, the metal goes back in the fire. One smith pulls out a bottle of beer, and they each take a swig. Then, out comes the glowing metal and the hammering resumes.",
            "year": 1893
        },
        {
            "_id": "573a1390f29313caabcd42e8",
            "plot": "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.",
            "title": "The Great Train Robbery",
            "fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
            "year": 1903
        },
…
```

Now the user in **group02** has access to only the four fields (**title**, **plot**, **fullplot**, and **year**), in addition to the **_id** field, as we configured in the role definition of a rule in App Services Rules.

### Step 4.3: Updating a role and a creating a filter in App Services Rules
Now we’ll update the existing role that we’ve created in [Step 4.2 by including **group03** to be evaluated, and we will add a filter that restricts access to only the movies where the **year** field is greater than 2000. 

Update the role (include **group03** in addition to **group02**) that you created in Step 4.2 as shown below.

Now, users that are in **group03** can authenticate and project only the four fields rather than all the available fields. But how can we put a restriction on the filtering based on the value of the **year** field? We need to add a filter.

Navigate to the **Filters** tab in the **Rules** page of the App Services after you choose the **sample_mflix.movies** collection. 

Provide the following inputs for the **Filter**:

After you’ve saved and deployed it, create a new JWT for the user **user03** that is in the group **group03**, as shown below:

Copy the encoded JWT and pass it to the curl command, as shown below:

```
curl --location --request POST 'https://ap-south-1.aws.data.mongodb-api.com/app/apitestapplication-ckecj/endpoint/data/v1/action/find' \
> --header 'jwtTokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMDMiLCJtZXRhZGF0YSI6eyJuYW1lIjoidXNlcjAzIiwiZ3JvdXAiOiJncm91cDAzIn0sImV4cCI6MTg5NjIzOTAyMiwiYXVkIjoiYXBpdGVzdGFwcGxpY2F0aW9uLWNrZWNqIn0._H5rScXP9xymF7mCDj6m9So1-3qylArHTH_dxqlndwU' \
> --header 'Content-Type: application/json' \
> --data-raw '{
>   "dataSource": "mongodb-atlas",
>   "database": "sample_mflix",
>   "collection": "movies",
>   "limit": 5
> }' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4008    0  3903  100   105   6282    169 --:--:-- --:--:-- --:--:--  6485
{
    "documents": 
        {
            "_id": "573a1393f29313caabcdcb42",
            "plot": "Kate and her actor brother live in N.Y. in the 21st Century. Her ex-boyfriend, Stuart, lives above her apartment. Stuart finds a space near the Brooklyn Bridge where there is a gap in time....",
            "title": "Kate & Leopold",
            "fullplot": "Kate and her actor brother live in N.Y. in the 21st Century. Her ex-boyfriend, Stuart, lives above her apartment. Stuart finds a space near the Brooklyn Bridge where there is a gap in time. He goes back to the 19th Century and takes pictures of the place. Leopold -- a man living in the 1870s -- is puzzled by Stuart's tiny camera, follows him back through the gap, and they both ended up in the present day. Leopold is clueless about his new surroundings. He gets help and insight from Charlie who thinks that Leopold is an actor who is always in character. Leopold is a highly intelligent man and tries his best to learn and even improve the modern conveniences that he encounters.",
            "year": 2001
        },
        {
            "_id": "573a1398f29313caabceb1fe",
            "plot": "A modern day adaptation of Dostoyevsky's classic novel about a young student who is forever haunted by the murder he has committed.",
            "title": "Crime and Punishment",
            "fullplot": "A modern day adaptation of Dostoyevsky's classic novel about a young student who is forever haunted by the murder he has committed.",
            "year": 2002
        },

…
```
Now, **group03** members will receive the movies where the **year** information is greater than 2000, along with only the four fields (**title**, **plot**, **fullplot**, and **year**), in addition to the **_id** field. 

## Summary
MongoDB Atlas App Services provides extensive functionalities to build your back end in a serverless manner. In this blog post, we’ve discussed:

* How we can enable Custom JWT Authentication. 
    * How we can map custom content of a JWT to the data that can be consumed in App Services — for example, managing usernames and the groups of users.
* How we can restrict data access for the users who have different permissions.
    * We’ve created the following in App Services Rules:
        * Two roles to specify read access on all the fields and only the four fields.
        * One filter to exclude the movies where the year field is not greater than 2000.

Can you add a call-to-action? Maybe directing people to our developer forums?

Give it a free try! Provision an M0 Atlas instance and create a new App Services Application. If you are stuck, let us help you in the [developer forums.