Using router in a plugin


#1

Hello,
I have a hard time implementing auth0 as their documentation is really weird.
I followed this tutorial which created a pluggin that provides authentication. It is much better than what is suggested in the documentation because you don’t have to import all the login functions all the time but can instead use $auth .

The problem is that whan you want to app to come back to the page it was before authentication you need to call the router inside the login function defined in the plugin:

login() {
      webAuth.authorize({
        state: this._route.path
      })
    },

but to have a way to call the router i needed to call it at the instantiation of the pluggin:

let auth = new Vue({
  router,
  computes...

but as soon as I do this i can’t call $auth anymore.

Can someone help me ?
best


#2

You might help us help you by axctually sharing the code of the plugin. the snippet your shared doesn’t ell us much of anything.


#3

ok so the plugin is in plugin/auth.js:

// import decode from 'jwt-decode';
import auth0 from 'auth0-js';
import Vue from 'vue'
import router from '../router'

const DOMAIN = ''

var config = {
  domain: DOMAIN,
  responseType: 'token id_token',
  scope: 'openid profile',
  audience: ''
}

if(process.env.NODE_ENV === 'development'){
  config['clientID'] = ''
  config['redirectUri'] = ''
} else {
  config['clientID'] = ''
  config['redirectUri'] = ''
}


var webAuth = new auth0.WebAuth(config);


let auth = new Vue({
  computed: {
    token: {
      get: function() {
        return localStorage.getItem('id_token')
      },
      set: function(id_token) {
        localStorage.setItem('id_token', id_token)
      }
    },
    accessToken: {
      get: function() {
        return localStorage.getItem('access_token')
      },
      set: function(accessToken) {
        localStorage.setItem('access_token', accessToken)
      }
    },
    expiresAt: {
      get: function() {
        return localStorage.getItem('expires_at')
      },
      set: function(expiresIn) {
        let expiresAt = JSON.stringify(expiresIn * 1000 + new Date().getTime())
        localStorage.setItem('expires_at', expiresAt)
      }
    },
    user: {
      get: function() {
        return JSON.parse(localStorage.getItem('user'))
      },
      set: function(user) {
        localStorage.setItem('user', JSON.stringify(user))
      }
    }
  },
  methods: {
    login() {
      webAuth.authorize({
        state: window.location.pathname
      })
    },
    logout() {
      localStorage.removeItem('access_token')
      localStorage.removeItem('id_token')
      localStorage.removeItem('expires_at')
      localStorage.removeItem('user')
      router.go('https://' + DOMAIN + '/v2/logout');
    },
    isAuthenticated() {
      return new Date().getTime() < this.expiresAt
    },
    isAdmin(){
      return this.user["https://example.com/roles"].includes("admin")
    },
    handleAuthentication() {
      return new Promise((resolve, reject) => {  
        webAuth.parseHash((err, authResult) => {

          if (authResult && authResult.accessToken && authResult.idToken) {
            this.expiresAt = authResult.expiresIn
            this.accessToken = authResult.accessToken
            this.token = authResult.idToken
            this.user = authResult.idTokenPayload
            resolve(authResult.state)

          } else if (err) {
            console.log(err)
            this.logout()
            reject(err)
          }

        })
      })
    }
  }
})

export default {
  install: function(Vue) {
    Vue.prototype.$auth = auth
  }
}

and my router.js:

import Vue from 'vue'
import Router from 'vue-router'
import Dashboard from './components/Dashboard.vue'
import Monitor from './components/Monitor.vue'
import Callback from './components/Callback.vue'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'dashboard',
      component: Dashboard
    },
    {
      path: '/monitor',
      name: 'monitor',
      component: Monitor
    },
    {
      path: '/callback',
      name: 'callback',
      component: Callback,
    }
  ]
})


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

  if(to.name == 'callback') {
    next()
  } else if (router.app.$auth.isAuthenticated() 
    // && router.app.$auth.isAdmin()
    ) {
    next()
  } else {
    router.app.$auth.login()
  }
})

export default router

and my main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import BootstrapVue from 'bootstrap-vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCoffee } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import ECharts from 'vue-echarts/components/ECharts'
import auth from './plugins/auth'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import VueSocketIO from 'vue-socket.io';

library.add(faCoffee)
Vue.component('font-awesome-icon', FontAwesomeIcon)
Vue.component('v-chart', ECharts)
Vue.component('form-autocomplete', require('./components/Autocomplete.vue').default); 
Vue.component('mynavbar', require('./components/Nav.vue').default); 
Vue.use(auth)
Vue.use(BootstrapVue);
Vue.use(new VueSocketIO({
    debug: true,
    connection: process.env.NODE_ENV === 'development' ? 'http://localhost:9990/cells' : '/cells'
}))
Vue.config.productionTip = false

window.APIurl = process.env.NODE_ENV === 'development' ? 'http://localhost:9990' : ''

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

i use the plugin in the nav component for example:

  <b-navbar-nav class="ml-auto">

        <b-button v-if="$auth.isAuthenticated()" @click="$router.push('/new')" size="sm" class="my-2 my-sm-0">New pack</b-button>

        <b-nav-item-dropdown right v-if="$auth.user">
          <template slot="button-content">
            <em><img :src="$auth.user.picture" width="30" height="30"></em>
          </template>
          <b-dropdown-item href="#">Profile</b-dropdown-item>
          <b-dropdown-item href="#" @click="$auth.logout()">Signout</b-dropdown-item>
        </b-nav-item-dropdown>

        <b-nav-item href="#" v-show="!$auth.isAuthenticated()" @click="$auth.login()">Login</b-nav-item>
      </b-navbar-nav>

my problem is that instead of state: window.location.pathname in login I think I should use router.path but i have no access to router


#4

Instead of directly creating the Vue instance in your plugin, wrap it in a function and only create it inside of the install function.

Then you can pass the router as a param to the plugin and add it to that Vue instance. Consequently, you can now access this.$route.

// other stuff

function createAuth(router) {
  return new Vue({
    //...

    router,

    methods: {
      login() {
        webAuth.authorize({
          state: this.$route.path
        })
      },
    // ...
  })
}

export default {
  install: function(Vue, router) {
    Vue.prototype.$auth = createAuth(router)
  }
}

Then use the plugin like this:

// main.js
import router from './router'
import auth from from './auth'

Vue.use(auth, router)

#5

thanks a lot for your help. I now a properly access to router.

For those interested, to get back to the original path after authentication with my example, I had to add an argument in the login function:

login(path) {
        webAuth.authorize({
          state: path
        })
      },

and in the router:

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

  if(to.name == 'callback') {
    next()
  } else if (router.app.$auth.isAuthenticated() 
    // && router.app.$auth.isAdmin()
    ) {
    next()
  } else {
    router.app.$auth.login(to.path)
  }
})

thanks again