DEV Community

Cover image for CurateBot Devlog 5: Adding Firebase Auth Logout and Vuetify Snackbars
Yuan Gao
Yuan Gao

Posted on

CurateBot Devlog 5: Adding Firebase Auth Logout and Vuetify Snackbars

To complete the auth system, we have a few loose ends to tie up: Logging out; navigation guards, and alerts. I'll quickly run over the code for these in this post, the corresponding code is here

login

Snackbar alerts

A lot of actions should give users some feedback about successes or failures. I will use snackbars to do it, that provide feedback about success or failure actions. I create a new alerts module in vuex store, because I am considering these alerts to be app-wide states, since they can be generated from anywhere.

The module just contains a series of Mutations to set up the state of the alert, which is defined by:

  • the message, the text to display
  • the color, which switches it between success and error and other colours
  • the isOpen boolean, which indicates whether the alert is displayed
  • the timeout value, which indicates how long this alert should stay onscreen

Then, I create a component that reads this state and renders it.

https://github.com/meseta/curatebot/blob/be289d9bd3d91ecdea1ab6d75ae0c76982788a1f/web/src/store/alert/index.ts
https://github.com/meseta/curatebot/blob/be289d9bd3d91ecdea1ab6d75ae0c76982788a1f/web/src/components/Alerts.vue

<template>
  <v-snackbar
    v-model="alertState.isOpen"
    :color="alertState.color"
    :timeout="alertState.timeout"
  >
    {{ alertState.message }}
    <template v-slot:action="{ attrs }">
      <v-btn
        text
        v-bind="attrs"
        @click="close()"
      >
        Close
      </v-btn>
    </template>
  </v-snackbar>
</template>

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { State, Mutation } from 'vuex-class';
import { AlertState } from '@/store/alert/types';
@Component
export default class SnackbarComponent extends Vue {
  @State(state => state, { namespace: 'alert'}) alertState!: AlertState;
  @Mutation('close', { namespace: 'alert'}) close!: Function;
}
</script>
Enter fullscreen mode Exit fullscreen mode

This Vue module uses vuetify's component, and largely just sticks the various pieces of data from the vuex state into it.

I think perhaps this is one area that could be simplified by using composition API/proxies, or indeed just events; however vuex does fine as well. You could make an argument that splitting the display of alerts into a component, and the state/mutations for the alert in vuex is an unnecessary split. I'll explore better ways of doing this in future.

Logout

Logging out is straightforward, there's a single function to do it: firebase.auth().signout(), but to keep things clean, I also need to clear the user's UID in the state store, as well as pop up an alert to notify of a successful logout, and redirect users to the homepage in case they were on a page that requires login. So a new action is added to the vuex module for auth:

  logout({commit}) {
    firebase.auth().signOut()
    commit('setUid', null);
    commit('alert/showSuccess', "Logged out", {root: true})
    router.push('/').catch(err => err);
  },
Enter fullscreen mode Exit fullscreen mode

The commit('alert/showSuccess') here uses the vuex module we set up earlier for snackbars; and router.push() is part of vue-router, and sends the user to the home page.

Navigation guards

Finally, certain pages require a login, so we need to prevent users from accessing the pages without being logged in (if they're not logged in, certain pages would break because of having no UID). Vue-router has a feature called Navigation Guards which prevent or allow loading of a route depending on certain rules you can set.

I add some new routes, and add an requiresAuth meta property to each route that requires log-in, in my routes file, then I add a navigation guard that checks for this requiresAuth property as well as the vuex auth/isAuthenticated getter which I set up previously:

router.beforeEach((to, from, next) => {
  document.title = to.meta.title

  const requiresAuth = to.matched.some(record => record.meta.requiresAuth)

  if (requiresAuth && !store.getters['auth/isAuthenticated']) {
    store.commit('alert/showError', "Please log in");
    next('/');
  } else {
    next();
  }
})
Enter fullscreen mode Exit fullscreen mode

In plain english: if the page requires authentication, and we're not already authenticated, then show an error "Please log in" and navigate to / (home) rather than the destination we were trying to go.

User profile pic display

If you were following my code closely, you may have noticed that I defined my UserData interface like this:

export interface UserData {
  profileImage: string;
  name: string;
  handle: string;
  id: string;
  accessToken: string;
  secret: string;
}
Enter fullscreen mode Exit fullscreen mode

And when the user actually does a login, I capture these details like this:

      const userData: UserData = {
        profileImage: profile.profile_image_url_https,
        name: profile.name,
        handle: profile.screen_name,
        id: profile.id_str,
        accessToken: credential.accessToken,
        secret: credential.secret
      }
      commit('setUid', uid);
      commit('setUserData', userData);
Enter fullscreen mode Exit fullscreen mode

This allows me to add a little logged-in user display to show the logged-in user in the top right corner of the app-bar!

    <v-sheet v-if="isAuthenticated" color="rgba(0,0,0,0)">
    <v-list-item
      dense 
      two-line
    >
      <v-list-item-content>
        <v-list-item-title class="text-right">{{userData.name}}</v-list-item-title>
        <v-list-item-subtitle class="text-right">@{{userData.handle}}</v-list-item-subtitle>
      </v-list-item-content>
      <v-list-item-avatar>
        <v-avatar>
          <img :src="userData.profileImage" :alt="userData.name">
        </v-avatar>
      </v-list-item-avatar>
    </v-list-item>
    </v-sheet>
Enter fullscreen mode Exit fullscreen mode

Working together, all these little features make logging in/out a more streamlined experience. And especially with the Vue dev-tools extension in the browser, you can scroll through vuex changes and inspect those states and transitions

Login sequence

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay