Quickly add role-based access control (RBAC) to a blog dashboard in Nuxt.js
Tags: javascript, nuxt, appwrite, vue
Published: 2022-12-23 09:31:34

Role-based access control (RBAC) is a form of system security that restricts unauthorized people from gaining access to some system’s data. It helps to protect not only data, but it also separate provides users the data needed to get their work done. 

RBAC for an entry-level employee or intern could mean restricting access to any of the company’s services that could be potentially at risk if not handled properly.

Appwrite is a self-hosted backend-as-a-service platform that provides developers with all the core APIs required to build any application. Appwrite delivers solutions that help building backend servers for applications. 

This article discusses adding RBAC to a blog dashboard in Nuxt.js using [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog).


## GitHub

Check out the complete source code [here](https://github.com/MoeRayo/appwrite-rbac).


## Prerequisites

Understanding this article requires the following:

- Installation of Node.js
- Basic knowledge of JavaScript
- Docker [installation](https://www.docker.com/get-started/)
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance locally or via one-click install on [DigitalOcean](https://marketplace.digitalocean.com/apps/appwrite) or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod)

## Creating our project

Use `npx create-nuxt-app <project-name>` to create a new Nuxt.js project. 
The process of scaffolding the project provides a list of options, which should look like this:

![Nuxt.js create app options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nllzbfkm32qz6xmpjbfy.png)


We can start our Nuxt3 application by running the following command:

```javascript
cd <project name>
npm run dev
```

Nuxt.js will, in turn, start a hot-reloading development environment that is accessible by default at `http://localhost:3000`.

### Adding the blog dashboard template
To add RBAC to an application, we will grab an off-the-shelf dashboard that we will modify to meet our goal. Our off-the-shelf template will be the [dashboard example](https://getbootstrap.com/docs/5.2/examples/dashboard/) from the Bootstrap website.

By the time we are through with customization, the dashboard should look like the image below. 

![The dashboard template](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cit30tu4x9vao91rd4wv.png)

We can follow the progress by switching to the `starter-app` branch in this [GitHub repository](https://github.com/MoeRayo/appwrite-rbac).


## Installing Appwrite

To use Appwrite in our project, we will install the Appwrite SDK from the command line, like so:

```javascript
npm install appwrite
```

## Creating an Appwrite project

To create a new project, we will start up the Appwrite instance on our machine and navigate to the specified hostname and port `http://localhost:80`. Next, we need to log into our account or create an [account](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=blog&utm_campaign=hackmamba) if we don’t have one.


![Appwrite sign up page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75re6hroccslxjgb1wdq.png)


Learn more about setting up Appwrite [here](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba). On the console, click on the **Create Project** button.

!Appwrite project creation page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/enjfmgfj9sih7gz0ekrc.png)

The project dashboard becomes visible on the console after the project creation. We can access the inner page from the Settings tab at the bottom of the page.

![Appwrite project setting](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l51h87uvr6xg9rlpi1yw.png)

The inner page allows us to copy the **Project ID** and **API Endpoint** for our Nuxt application.


![Appwrite project overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhszk2c35irp2x9jehnb.png)

We will then proceed to create an `init.js` file in our Nuxt.js application’s root directory to initialize the Appwrite Web SDK by adding the code block below:


```javascript
import { Client, Account } from "appwrite";
export const client = new Client();
export const account = new Account(client);
client
    .setEndpoint('http://localhost/v1') // API Endpoint
    .setProject('***') // project ID
;
```

## Creating the signup page

We will create a signup page where people who will later be assigned roles to perform certain operations within the database can generically create an account. To do this, we will create  a `components/Signup.vue` and add the code below:

```javascript
<template>
  <div class="">
    <main class="form-signin w-100 mx-auto py-5 px-5 my-5">
      <form class="mt-5" @submit.prevent="signUp">  
        <h1 class="h3 mb-4 fw-normal text-center">Admin Dashboard</h1>
        <div class="form-floating mb-3">
          <label for="floatingInput">Username</label>
          <input type="username" v-model="username" class="form-control" id="floatingInput" placeholder="Username">
        </div>
        <div class="form-floating mb-3">
          <label for="floatingInput">Email address</label>
          <input type="email" v-model="email" class="form-control" id="floatingInput" placeholder="name@example.com">
        </div>
        <div class="form-floating mb-4">
          <label for="floatingPassword">Password</label>
          <input type="password" v-model="password" class="form-control" id="floatingPassword" placeholder="Password">
        </div>
        <div class="form-floating mb-4">
          <label for="floatingPassword">Confirm Password</label>
          <input type="confirm-password" v-model="confirmPassword" class="form-control" id="floatingPassword" placeholder="Confirm Password">
        </div>
    
        <button class="w-100 btn btn-lg btn-primary" type="submit">Get Access</button>
        <p class="mt-3">Already have an account? <nuxt-link to="/signin">Sign in</nuxt-link> </p>
      </form>
    </main>
  </div>
</template>
<script>
export default {
  data: () => ({
    username: "",
    email: "",
    password: "",
    confirmPassword: ""
  }),
}
</script>

<style scoped>
.form-signin {
  max-width: 400px;
  padding: 15px;
  border: 5px solid #0069D9;
}
.form-signin .form-floating:focus-within {
  z-index: 2;
}
.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
</style>
```
Implementing the code block above, we achieved the following:

- Created a functional form to handle input from potential users of our dashboard
- Added the `data` in the Vue script to manage the username, email, and password of the people who will fill out the form

### Adding the signup functionality 
Our interface will need to be connected to Appwrite to authenticate users. To do this, we will add the following code in the `<script>` tag of our `components/Signup.vue` file.

```javascript
<script>
import {account} from '~/init'
export default {
  data: () => ({
    // data goes here
  }),
  methods: {
    signUp: async function(){
      if (this.password.length >= 8){
        if(this.password === this.confirmPassword) {
          try{
            await account.create('unique()', this.email, this.password, this.username)
            alert("account created successfully")
            window.location.href = '/signin'
          } catch (e) {
            console.log(e)
          }
        } else {
          alert("password do not match")
        }
      } else {
        alert("password length should be up to 8 characters")
      }
    },
  }
}
</script>
```
The code above does the following:

- Imports the Appwrite SDK initialized in the `init.js` file.
- Creates the `signUp` function in the `methods` property, which performs the following functions:
    - Verifies that the password length is equal to or greater than eight characters.
    - Confirms that the characters in `password` and `confirmPassword` are the same.
    - Accesses Appwrite’s services using the `account.create` to create a new account using the user’s email, password, and name.
        - To use Appwrite’s create account service, it’s mandatory to add the `userId`; in this case, we used `uniqueId` to generate one automatically.
        - Adding `email` and `password` is also mandatory.
        - The `name` option is optional, and we can create an account without one.
    - `window``*.location.*``href` allowed us to navigate to the chat interface after successful account creation.

Next, let's import the `components/Signup.vue` into the `pages/index.vue`, like so: 

```javascript
<template>
  <div>
    <Signup/>
  </div>
</template>
```
At this point, our signup page should look like the below:

![The signup page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyn8mddt699jd8xr1orn.png)

## Creating the sign-in interface

We also need to create a sign-in page that allows already registered users to log in to the dashboard. To do this, we will create a `pages/Signin.vue` and add the code below:

```javascript
<template>
  <div class="">
    <main class="form-signin w-100 mx-auto py-5 px-5 my-5">
      <form class="mt-5" @submit.prevent="signIn">  
        <h1 class="h3 mb-4 fw-normal text-center">Admin Dashboard</h1>
        <div class="form-floating mb-3">
          <label for="floatingInput">Email address</label>
          <input type="email" v-model="email" class="form-control" id="floatingInput" placeholder="name@example.com">
        </div>
        <div class="form-floating mb-4">
          <label for="floatingPassword">Password</label>
          <input type="password" v-model="password" class="form-control" id="floatingPassword" placeholder="Password">
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit">Get Access</button>
        <p class="mt-3">New? Create an account? <nuxt-link to="/">Sign up</nuxt-link> </p>    
      </form>
    </main>  
  </div>
</template>
<script>
import {account} from '~/init'
export default {
  data: () => ({
    email: "",
    password: "",
  }),
}
</script>
```
The code block above achieves the following:

- Creates a functional form to handle input from registered users 
- Adds the `data` in the Vue script to manage the email and password
### Adding the sign-in functionality 
Our interface will need to be connected to Appwrite to authenticate users for signing in. To do this, we will add the code below in the `SigninView.vue`. 

```javascript
<script>
import {account} from '~/init'
export default {
  data: () => ({
    email: "",
    password: "",
  }),
  methods: {
    signIn: async function () {
      try{
        let accountDetails = await account.createEmailSession(this.email, this.password)
        alert("user signed in")
        this.$router.push({ path: `/dashboard/${accountDetails.userId}`})
      } catch (e){
        console.log(e)
      }
    },
  }
}
</script>
```
Above, we imported the Appwrite SDK initialized in the `init.js` file. We also created a function called `signIn` that does the following:

- Accesses Appwrite’s services using the `account.createEmailSession` to create a new account session using the user’s email and password 
- `this.$router.push` allows us to navigate to the chat interface after successful user login

At this point, our interface will look like this:

![The sign-in interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyqdq2h2zgckss6hjl5c.png)


## Adding user role on the Appwrite dashboard

Appwrite allows us to give users role-based access to perform either or all of these functions: 

- Read 
- Read and write
- Read, write, and delete 

To do this, we will create three users from our signup page and give them different role access from Appwrite.

### Creating users
We will create one user here for demo purposes to show us how the signup process works.


![Creating users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u0836so31vv6yzn2spu.gif)


We will do this for two more users. Afterward, we will navigate to our appwrite dashboard to add the permissions for each user.


## Setting up Appwrite’s database

One of the services offered by Appwrite is the **Database Service**, which allows us to store data and fetch it as needed. 

### Creating a database
To create a database in Appwrite, click on the **Database** option in the menu tab, where we can add a database to the system.


![Creating a database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/forqcg5qdihko4c00c5g.png)



### Creating a collection
Appwrite’s database services also provide collection services. These collections act as containers for documents. To create a collection, select the desired database from which we can add a new collection.

![Creating a collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gt3r0weubtz6mqc3kely.png)

After creating a new collection, the system redirects us to the **Settings** page, where we can set the top-level permissions needed to access the database service. In our case, we will allow **document security**, allowing users to access only the documents they have express permission to access.


![Collection dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slssuwxmw9czc5rs7x5b.png)

### Creating attributes
Our document will have some peculiar attributes. To create them, we will navigate to the **Attributes** section. In our case, we will choose the **New String Attribute.**


![Creating attributes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6u1in2igv6tjdcz9udo5.png)

We will also add an **Attribute Key** named “message” with a size of 256 bits.

Next, we will create a document from where we can add the document permissions.

![creating document](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwrcwbfkmd4f26ejxadv.png)


From the newly created document, we can set roles and permissions.
We will click on the **permissions** tab, and in the **Role** input field, we will pick the role that best describes to whom we are giving access. In our case, the role is **select users**, as this allows us to assign roles to only specific users. We will match this role with the corresponding user id from some of the already-created users in our database.


![Setting roles and permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sozcq4lifzp455jbzbx2.gif)


We will create three different roles, and at the end, we should have our documents permission looking like the below:


![Documents permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgn559gl7em5bosbh5jt.png)


## Connecting the dashboard to Appwrite’s database

We will navigate to the `dashboard.vue` file and import the Appwrite’s SDK initialized in the `init.js` and Appwrite’s `database` method. We will also create an instance of `databases` using the imported `databases` method from Appwrite, which takes the client as a parameter. Like so:

```javascript
import {client} from '~/init'
import { Databases } from 'appwrite';
const databases = new Databases(client);
```
We will also update the `data` property in the `script` tag with the following options that will add state changes to the dashboard. Like so:

```javascript
data() {
  return {
    permisssionsArray: [],
    denyActive: false,
    dashboardActive: false,
    orderActive: false,
    reportActive: false,
  }
}
```

### Get documents permissions from the database
Now, we can use the `getDocumentPermissions` function to get document permission:


```javascript
mounted(){
  this.getDocumentPermissions()
},
methods: {
  getDocumentPermissions: async function() {
    let promise = await databases.getDocument('6381e4342875d287ab7b', '6381e44a94d7c36be8f9', '6385d5b030814a0a9b78');
    const match = promise.$permissions.find(element => {
      if (element.includes(this.$route.params.dashboard)) {
        this.permisssionsArray.push(element)
        let newPermission = ''
        for (let i = 0; i< this.permisssionsArray.length; i++){
          newPermission += this.permisssionsArray[i]
        }
      
        if(newPermission.includes('read') && newPermission.includes('update') && newPermission.includes('delete')){
          this.reportActive = true;
        } else if(newPermission.includes('read') && newPermission.includes('update')){
          this.orderActive = true;
          this.dashboardActive = true;
        } else if(newPermission.includes('read')){
          this.dashboardActive = true;
        } 
      }
    });
  },
},
```
We achieved the following from the above code block:

- Created the `getDocumentPermissions` method, which accesses the `databases` method from Appwrite to get all documents in the database.
- The `getDocumentPermissions` method takes the `database, collection`, and `Document ID` as parameters. These IDs can be found on the document dashboard on Appwrite.
- Returned documents from the Appwrite database are stored in the `promise` variable.
- Checked whether the user ID in the permissions object matches the current user. If this is true, we create a `permissionsAray` that contains all the permission for that user.
- Looped through the array to concatenate the user permissions for easy identification of the proper permissions for interface access.
- Created conditions based on the user’s permission. 

At this point, a user with read access permission can only see the dashboard navigation.


![Users with only the read access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzjd7ybcu9uhf4naipxo.gif)

A user with **read** and **update** permissions can see the dashboard navigation and the navigation links to check orders.


![User with the read and update access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/696edi8nfm2t3khjaddp.gif)


Likewise, a user with the read, update, and delete permissions will be able to see the dashboard, the order reports, and the saved reports. Like so:


![User with read, update and delete access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayq3029bjqovfs4a8t7y.gif)

## Conclusion

This article discusses adding role-based access control to a dashboard using Appwrite’s database and permissions features.

## Resources
- [Create a local Appwrite instance in 3 steps](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) 
- [Appwrite’s account API](https://appwrite.io/docs/client/account?sdk=web-default#accountCreateOAuth2Session?utm_source=hackmamba&utm_medium=blog&utm_campaign=auth0_auth0-authentication-vue)
- [Appwrite’s](https://appwrite.io/docs/permissions) [permission documentation](https://appwrite.io/docs/permissions)