Keeping track of the authentication state with Pinia in a Vue.js web app
Published: 2024-04-30 13:52:48

Once a user is registered, or logged in in most cases the dashboard or navigation links will slightly change in an application to reflect the logged-in state. This was something that during my project I also wanted to achieve. To keep track of the authentication state I used Pinia state management and created a user store. By doing this I was able to conditionally render different components/html elements or content depending on the authentication state.

The first step was to create a Pinia store and initialise the authentication state as false.
```javascript
import { defineStore } from 'pinia'

export default defineStore('user', {
  state: () => ({
    userLoggedIn: false
  }),
})
```

In a previous blog post, I described how I registered users using Firebase (link to the blog post: https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36), however, this solution was isolated to a single component. I wanted to have easy access to the register function and I wanted to keep track of the authentication state. 

This is where Pinia's actions property came in handy. Pinia actions are functions that outsource business logic, they become available to all components. Actions can be asynchronous and can commit multiple mutations which helps reduce the amount of code you have to write. 

I decided to extract the register function logic from the component and add it to Pinia actions, as this function plays a role in updating the authentication state. Once a user registered I wanted to give access to their personal dashboard and update the authentication state to true. I imported the necessary methods from Firebase and references to the Auth instance and Firestore database (find details on this here: https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36). After initialising the ‘userLoggedIn’ as false, I created an action function called register. To be able to access the ‘this’ keyword this function has to be written as a regular function, and not an arrow function.

```javascript
import { defineStore } from 'pinia'
import { auth, db } from '@/includes/firebase'
import { createUserWithEmailAndPassword } from 'firebase/auth'
import { doc, setDoc } from 'firebase/firestore'

export default defineStore('user', {
  state: () => ({
    userLoggedIn: false
  }),
  actions: {
    async register(values) {
      // logic to register user
      const userCred = await createUserWithEmailAndPassword(auth, values.email, values.password)
      const newDocument = doc(db, 'users', userCred.user.uid)
      await setDoc(newDocument, {
        name: values.name,
        email: values.email
      })

      // logic to update state
      this.userLoggedIn = true
    },
  }
})
```

In the code block above you can see that after the logic of registering a user, I updated the authentication state using the ‘this’ keyword. The keyword refers to the ‘userLoggedIn’ variable. The asynchronous function register receives as an argument the values object which contains the input data that the user entered.

In the component that has the register form, I replaced the register logic inside the try block with the action function imported from the Pinia user store. To be able to access this function I used the mapActions method from Pinia. The script looked something like this after the changes were made.

```javascript
<script>
import { mapActions } from 'pinia'
import useUserStore from '@/stores/user'

export default {
  name: 'AppRegister',
  methods: {
    ...mapActions(useUserStore, {
      createUser: 'register'
    }),
    async register(values) {
      try {
        await this.createUser(values)
      } catch (error) {
        return
      }
    }
  }
}
</script>
```

In case you are wondering where the 'values' argument comes from, I used VeeValidate to handle the form. In VeeValidate if you add a submit handler to the vee-form component, VeeValidate will automatically pass the form values to the handler as the first argument.