Build an authentication flow with Auth0 and Vue3
Tags: javascript, vue, tutorial, webdev
Published: 2021-10-24 17:34:22

### Introduction
In every project, you'll probably start building the login, registration, reset password functionality, well, Auth0 provides a set of tools that are going to help you to complete that kind of task faster than the traditional way.

In this guide, you'll create an entire authentication flow with Auth0 and Vue3.

By the end of this post, you'll have a vue app that allows users to register, login 
and logout and be able to use that knowledge to build your next project.
 
## Prerequisites
- [Node.js](https://nodejs.org/en/) installed in your machine at least version 12.20
- Knowledge of CSS.
- Previous experience with Vue.
- Basic understanding of [the composition API](https://dev.to/jesusantguerrero/from-my-point-of-vue-composition-api-18de)

## Step 1: Creating a new vue3 project
To create a new Vue3 project we use [vite](https://vitejs.dev/guide/) (pronounced 'vit') which is going to scaffold the structure with the latest version of vue and set the dependencies and provide a fast developer experience. 

Running the following code in your terminal will ask you for the name of the new project.


```sh
npm init vite@latest --template vue
```

Next go to the project dir in the terminal and install the dependencies with the following command:
```sh
cd project-dir && npm install
```

One last install, this time is the SDK of Auth0 for single-page applications

```sh
npm install @auth0/auth0-spa-js
```
Create a new file `.env.local` and type `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_DOMAIN` let it there and you'll be back later to this file to place those values from Auth0.
```.env
VITE_AUTH0_DOMAIN=
VITE_AUTH0_CLIENT_ID=
```

## Step 2: Create an Auth0 project
Before drop your first lines of code you'll need to create a new auth project for that:
1. Go to [Auth0](https://auth0.com/signup?place=header&type=button&text=sign%20up) and create an account
2. In the left side menu click on **Applications** dropdown then **Applications** option and then **Create Application**. This will open a modal to type the name and select an application type. ![Application Setup](https://cdn.hashnode.com/res/hashnode/image/upload/v1632185164677/-a5ROZu6Q.png) 
3. Select **Single page web applications** and give **VueAuth** as the application name, you can come back and change it later.
4. Go to settings tab in the newly created project and copy `Domain` and `Client ID` to `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID` respectively in `.env.local` file
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632185381832/Ka6unG0Ks.png)
5. Go down a little more until the **Application URIs** section and you need to set some routes to let Auth0 know where to go after certain events in this case our URL is `http://localhost:3000` and you need to type in  **Allowed Callback URLs** and **Allowed Logout URLs** and **Allowed Web Origins** like is showed in the picture below

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632224890619/XAqRlRRy2.png)

## Step 3: Creating useAuth0 composable.
It's time to drop some lines of code, as Vue3 gives us the power of reactivity even outside a component you are going to use that to wrap the Auth0 flow into its own file
create a new folder in `/src` called `utils/` and inside create a new file named `useAuth0.js`

In `/src/utils/useAuth0.js` you need to create a new reactive object to save the AuthState and it will be exported.

```js
// utils/useAuth0.js
import createAuth0Client from '@auth0/auth0-spa-js';
import { reactive } from 'vue';

export const AuthState = reactive({
    user: null,
    loading: false,
    isAuthenticated: false,
    auth0: null,
});
```
Next, in order to simplify the configuration management lets add a `config` constant and set the domain and client_id from our `.env.local` those are available using the keywords `import.meta.env.NAME_OF_VARIABLE` as follows:
```js
// utils/useAuth0.js
...
const config = {
    domain: import.meta.env.VITE_AUTH0_DOMAIN,
    client_id: import.meta.env.VITE_AUTH0_CLIENT_ID
};
```
Now the most important part, to wrap the authentication flow we are going to export an arrow function that takes the state as param, which will be the AuthState you made at the top of this file. And it's going to return three functions **login**, **logout** and **initAuth**

```js
export const useAuth0 = (state) => { 
 // The implementation will go here

  return {
     login,
     logout,
     initAuth
  }
}
```
Let's write a utility function it is not going to be returned but it will be called after **login**, **logout**, and **initAuth** it will be called `handleStateChange` and is going to pass the **authentication status** from Auth0 to you `AuthState`.
```js
export const useAuth0 = (state) => { 
   const handleStateChange = async () => {
        state.isAuthenticated = !!(await state.auth0.isAuthenticated());
        state.user = await state.auth0.getUser();
        state.loading = false;
    }

...
}
```
In the next function **initAuth** you'll create a new instance of `Auth0Client` and for that you need the configuration you saved before **domain**, **client_id**, cacheLocation and redirect_uri
- **domain **and **client_id**: are the tokens you saved in `.env.local`
- **cacheLocation**: Is where Auth0 stores the token, by default the value is 'memory' that is not going to persist after you reload the page, since we don't want this use `localstarage` that keeps the token even after refreshing the page.
- **redirect_uri**: Remember the routes you set before in the settings of your application in Auth0, well, you need it here with `window.location.origin` you get the current location of the browser that will be 'http://localhost:3000' the same you saved there.

After the `Auth0Client` is created, call the `handleStateChange` function to get the current authentication state.

```js
...
const initAuth = () => {
     state.loading = true;
     createAuth0Client({
          domain: config.domain,
          client_id: config.client_id,
          cacheLocation: 'localstorage',
          redirect_uri: window.location.origin
      }).then(async auth => {
          state.auth0 = auth;
          await handleStateChange();
      });        
}
```
Next, the login, auth0 has a loginWithPopup that is going to open a popup and ask the user for the credentials to login or register after.
```js
...
const login = async () => {
    await state.auth0.loginWithPopup();
    await handleStateChange();
};
```
Next, the logout, auth0 has a logout function that accepts an object as an argument and a **returnTo** property is required. Here you can type your current location with `window.location.origin`.
```js
...
const logout = async () => {
    state.auth0.logout({
          returnTo: window.location.origin,
    });
}
```

By now your `src/utils/useAuth0.js` file should look like this:
```js
// utils/useAuth0.js
import createAuth0Client from '@auth0/auth0-spa-js';
import { reactive } from 'vue';

export const AuthState = reactive({
    user: null,
    loading: false,
    isAuthenticated: false,
    auth0: null,
});

const config = {
    domain: import.meta.env.VITE_AUTH0_DOMAIN,
    client_id: import.meta.env.VITE_AUTH0_CLIENT_ID
};

export const useAuth0 = (state) => {
    const handleStateChange = async () => {
        state.isAuthenticated = !!(await state.auth0.isAuthenticated());
        state.user = await state.auth0.getUser();
        state.loading = false;
    }

    const initAuth = () => {
        state.loading = true;
        createAuth0Client({
            domain: config.domain,
            client_id: config.client_id,
            cacheLocation: 'localstorage',
            redirect_uri: window.location.origin
        }).then(async auth => {
            state.auth0 = auth;
            await handleStateChange();
        });        
    }

    const login = async () => {
        await state.auth0.loginWithPopup();
        await handleStateChange();
    };

    const logout = async () => {
        state.auth0.logout({
            returnTo: window.location.origin,
        });
    }

    return {
        login,
        logout,
        initAuth,
    }

}
```

## Step 4: Setup App.vue
Let's modify the `src/App.vue`.

Take a look at the final code of the `App.vue` I'll explain bellow.

```html
<script setup>
import { useAuth0, AuthState } from "./utils/useAuth0";
const { login, logout, initAuth } = useAuth0(AuthState);

initAuth();
</script>

<template>
  <div v-if="!AuthState.loading">
    <img alt="Vue logo" src="./assets/logo.png" />
    <div v-if="!AuthState.isAuthenticated">
      <button @click="login()" class="btn btn-primary">Login</button>
    </div>

    <div v-else>
      <p> Welcome to VueAuth <strong>{{ AuthState.user.name }}</strong></p>
      <button @click="logout()" class="btn btn-secondary">Logout</button>
    </div>
  </div>

  <div v-else>
    Loading ...
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

.btn {
  padding: 8px 12px;
  margin-bottom: 0;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  border: none;
  cursor: pointer;
  min-width: 100px;
  border-radius: 4px;
  font-weight: bold;
}

.btn-primary {
  background: #41B883;
  color: white;
}

.btn-secondary {
  background: #aaa;
  color: white;
}
</style>
```

At the top of the file in the script section **AuthState** and **useAuth0** are imported from the wrapper you created.

The **AuthState** is used to call **useAuth0** and get the **login**, **logout** and **initAuth** functions.

And at the end of the script **initAuth()** is called to create the instance and get the current authentication state of the user.

```html
<script setup>
import { useAuth0, AuthState } from "./utils/useAuth0";
const { login, logout, initAuth } = useAuth0(AuthState);

initAuth();
</script>
```
In the template section we check if the app is loading and if the user is not authenticated show the `login` button that calls the **login** function in the **script** but if it is authenticated show the user name and a logout button calling the **logout** function from the **script**.

If the app is loading it shows the `loading...` text.

```html
<template>
  <div v-if="!AuthState.loading">
    <img alt="Vue logo" src="./assets/logo.png" />
    <div v-if="!AuthState.isAuthenticated">
      <button @click="login()" class="btn btn-primary">Login</button>
    </div>

    <div v-else>
      <p> Welcome to VueAuth <strong>{{ AuthState.user.name }}</strong></p>
      <button @click="logout()" class="btn btn-secondary">Logout</button>
    </div>
  </div>

  <div v-else>
    Loading ...
  </div>
</template>
```
### Final result
<a href="https://www.loom.com/share/e1ab06e093bb407c990f2fbd23cc64d5">
    <p>VueAuth - Watch Video</p>
    <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/e1ab06e093bb407c990f2fbd23cc64d5-with-play.gif">
  </a>


## Conclusion
You built an authentication flow with Vue3 and Auth0, congrats! Now that you are familiar with **Auth0** and its benefits you are able to implement it in your next project.

Thanks for reading. If you have any questions the comments are open, or if you like [Twitter](https://twitter.com/JesusntGuerrero) as well as my [Github](https://github.com/jesusantguerrero) where I do some experiments and projects.

Have a good day.
## Resources
- [Repo of this guide](https://github.com/jesusantguerrero/vue-auth0)
- [Auth0 SDK Docs](https://auth0.com/docs/libraries/auth0-single-page-app-sdk)