Part 1: User Roles and Management - Quasar
Tags: javascript, vue, webdev, quasar
Published: 2020-09-02 12:49:44

## The Quasar Frontend
I chose the [Quasar Framework](https://quasar.dev) for its abundance of out-of-the-box components. I've used it for several prototyping projects already in the past, and it allows for rapid development, which is especially useful for prototyping. I didn't focus too much on the frontend for this starter, as the frontend ends up being highly customized for the client's brand and needs anyway, so I just focused on functionality. As a result, I left some unneeded scaffolded code as-is which provides no value to this starter in its current state.

## Getting Started
I'm going to assume some basic understanding of Vue, Vuex, VueRouter, and Quasar. I'm not going into specifics for how the Quasar framework is set up, but I'll cover some customizations for this project. If you're new to [Vue.js](https://vuejs.org) or [Vuex](https://vuex.vuejs.org/), it's recommended to do a project or two with Vue before jumping into [Quasar's Getting Started Guide](https://quasar.dev/start/pick-quasar-flavour). 

> Note: I would advise against copying/pasting any code snippets and instead go directly to the repository to view the code. Due to the amount of code involved, I've omitted lines of code in this article for brevity. It's not written as a tutorial, so please view the repository if you want to create a similar project.

### Additional Libraries
In addition to Quasar, I also used several libraries for additional functionality: 

* [FeathersJS Client Libraries](https://docs.feathersjs.com/api/client.html#node)
* [Axios](https://github.com/axios/axios)
* [Feathers-Vuex](https://vuex.feathersjs.com/)
* [Vuelidate](https://vuelidate.js.org/)

### The Views
I only needed a few views to implement the various backend functions planned for this starter:

* Register
* Login
* Reset Password
* Verify Email
* Account
* Forgot Password
* Admin Dashboard
* Home

Once I identified which views were needed, I created the [pages](https://github.com/meditatingdragon/quasar-feathersjs-user-management/tree/master/client/src/pages) for each view and added it to the [routes](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/router/routes.js). Most pages consist of forms to allow data entry. There isn't too much complexity in the UI, as most of them are forms, so I won't go into too much detail, but let me know if you have any questions!

### Validation
I used the [vuelidate](https://vuelidate.js.org/) library for form validation. To add it to Quasar, I used a [boot file](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/boot/vuelidate.js). 

Here's an example checking the email in the [Login](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/pages/Login.vue):

```vue
<q-input 
   outlined
   v-model="$v.formData.email.$model"
   label-color="accent"
   label="Email"
   type="email"
   lazy-rules
   dense
   class="q-ma-sm"
   :error="$v.formData.email.$invalid && 
   $v.formData.email.$dirty"
            >
      <template v-slot:prepend>
          <q-icon color="accent" name="fas fa-envelope" size="xs" />
      </template>
      <template v-slot:error>
          <div v-if="!$v.formData.email.required">
              This field is required.
          </div>
          <div v-if="!$v.formData.email.email">
              Please provide a valid email address
          </div>
      </template>
</q-input>
```

```js
import { required, minLength, email } from "vuelidate/lib/validators";

export default {
  data() {
    return {
      formData: {
        email: ""
      }
    };
  },
  validations: {
    formData: {
      email: {
        required,
        email
      }
    }
  },
}
```

Vuelidate made it pretty easy to check form values with minimal custom code, which I really appreciated (I'm a lazy dev...). 

### FeathersJS Client
The FeatherJS framework provides several [client libraries](https://docs.feathersjs.com/api/client.html#node) that can be used to handle authentication and service calls. I incorporated this so I wouldn't have to implement the authentication on my own. 

To use the feathers client library, I added a [feathersClient.js](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/boot/feathersClient.js) boot file, which pulled in my axios configuration settings stored in [axios.js](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/boot/axios.js). The axios configuration enables the display of a loading bar when a request is made. 

I also [setup Feathers-Vuex](https://vuex.feathersjs.com/getting-started.html#feathers-client-feathers-vuex) with the feathersClient boot file:

```js
// Setting up feathers-vuex
const {
  makeServicePlugin,
  makeAuthPlugin,
  BaseModel,
  models,
  FeathersVuex
} = feathersVuex(feathersClient, {
  serverAlias: "api", // optional for working with multiple APIs (this is the default value)
  idField: "_id", // Must match the id field in your database table/collection
  whitelist: ["$regex", "$options"]
});

export { makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex };
```

This provides [basic authentication handling](https://vuex.feathersjs.com/auth-plugin.html#actions) via the following vuex actions: `auth/authenticate` and `auth/logout`.

### Route Guards
Additionally, to handle some access-based views, I created an [auth.js](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/boot/auth.js) boot file which redirects users as needed based on their authorized user status.

```js
router.beforeEach((to, from, next) => {
    if (to.meta.requiresAuth) {
      // if requires admin
      if (store.state.auth.user) {
        if (to.meta.requiresAdmin) {
          if (
            store.state.auth.user.permissions &&
            store.state.auth.user.permissions.includes("admin")
          ) {
            next();
          } else {
            Notify.create({
              message: `Your account is not authorized to see this view. If this is in error, please contact support.`,
              color: "negative"
            });
            next("/account");
          }
        } else if (
          to.path === "/" ||
          to.path === "/login" ||
          to.path === "/register"
        ) {
          next("/account");
        } else if (!LocalStorage.getItem("feathers-jwt") && to.path !== "/") {
          next("/login");
        } else {
          next();
        }
      } else {
        if (to.path !== "/login") {
          next("/login");
        } else {
          next();
        }
      }
    } else {
      next();
    }
```

I added in metadata to specify the need for the administrative user status for admin routes.

```js
{
    path: "/admin",
    component: () => import("layouts/MainLayout.vue"),
    children: [
      {
        path: "",
        name: "AdminHome",
        component: () => import("pages/admin/Index.vue"),
        meta: { requiresAuth: true, requiresAdmin: true }
      }
    ]
  },
```

If the route required admin access, it would check if the user held the necessary permissions to access the route. 

### Navigation
The default layout [MainLayout](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/layouts/MainLayout.vue) was updated to include several navigation links. For a logged-in user, navigation becomes available for the account, admin (if the user is admin), and logout. For an unauthorized user, login and register links become available.

### App.vue
The [App.vue](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/App.vue) entry point leverages the Vue 3 Composition API (which is also added to Quasar via a [boot file](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/boot/composition.js)) to authenticate a user if a JWT token is stored when the app first loads. It also checks for a change in the user's authorization status and will redirect the user if they login/logout.

### Services
I created a user service to help with making requests. For the feathers client, I added a [folder](https://github.com/meditatingdragon/quasar-feathersjs-user-management/tree/master/client/src/services/feathers-client) for feathers specific services, which would mirror the server-side feathers service. I didn't add any customization other than to configure the client-side service to know which server-side service it would need to communicate with:

```js
const servicePath = "users";
const servicePlugin = makeServicePlugin({
  Model: User,
  service: feathersClient.service(servicePath),
  servicePath
});
```

This is with the [Feathers-Vuex Service Plugin](https://vuex.feathersjs.com/service-plugin.html#setup). This service should be customized to your user service needs, which this starter does not do.

For the application user service, which provides a service layer for executing requests from FeathersClient or Axios, I separated the [user service](https://github.com/meditatingdragon/quasar-feathersjs-user-management/tree/master/client/src/services/user) into [admin](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/services/user/admin.js) and [account](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/services/user/account.js) service calls for modularity. 

For simplicity, I used axios to make the few unauthenticated calls (creating a user, etc.) needed in this starter, though for production, feathers client should probably be used for all calls. 

The service calls would look like this:

```js
export async function updateUser(user, id) {
  return feathersClient.service("users").patch(id, user);
}
```
or 
```js
export async function updateIdentity(password, currentEmail, updatedEmail) {
  return axiosInstance.post("/authManagement", {
    action: "identityChange",
    value: {
      password: password,
      changes: { email: updatedEmail },
      user: {
        email: currentEmail
      }
    }
  });
}
```

> Edit: After working with Feathers-Vuex more, I realized how powerful it actually is and would re-do the service layer in this example to completely use feathers-vuex rather than have a separate user service to handle some other service requests. Definitely a case of read the documentation before using it! 

The FeathersJS backend uses [feathers-authentication-management](https://github.com/feathers-plus/feathers-authentication-management/blob/master/docs.md) and [feathers-permissions](https://github.com/feathersjs-ecosystem/feathers-permissions). These service files demonstrate the necessary payload to send to the FeatherJS backend for the front-end implementation of the feathers-authentication-management library, which will be discussed in greater depth in the next article.

### State Management
The vuex store uses Feathers-Vuex to keep the store synchronized with Feathers Client requests. For authentication, there is a [store.auth.js](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/store/store.auth.js) file in the root directory to configure the Feathers client authentication service to the users service:

```js
import { makeAuthPlugin } from "../boot/feathersClient";
export default makeAuthPlugin({ userService: "users" });
```
This is imported in the index as `authvuex`.

Feathers-Vuex is setup in the store [index](https://github.com/meditatingdragon/quasar-feathersjs-user-management/blob/master/client/src/store/index.js) as follows:

```js
import { FeathersVuex } from "../boot/feathersClient";
import authvuex from "./store.auth";

const servicePlugins = requireModule
  .keys()
  .map(modulePath => requireModule(modulePath).default);

Vue.use(FeathersVuex);

export default function(/* { ssrContext } */) {
  const Store = new Vuex.Store({
    plugins: [...servicePlugins, authvuex],
    modules: {
      account,
      admin
    },
    strict: process.env.DEV
  });

  return Store;
}
```

The store is namespaced with admin and account modules, mirroring the separation in the user service. The `action.js` files make all the necessary service calls and handle the result of the call. A default state is defined, and some mutations are defined to handle the results of the service calls.

#### Did I Miss Anything?

I think this covers the key updates for building out a Quasar frontend to work with a FeathersJS backend. Let me know if I left anything out. Leave a comment with questions, suggestions, etc. I'll update this article accordingly!