Skip to content

Authentication

David Graham edited this page Jun 26, 2018 · 5 revisions

In this section of the tutorial we'll look at Authentication and OAuth2...

Setup

The methods we need for Authentication in our app are the following:

  • install : This allows us to simply use Vue.use(auth) in our main.js entry to setup authentication.
  • setDefaults : Setup any defaults we need.
  • addInterceptors : We will need to intercept all auth http calls to do things like setting headers, refreshing tokens, and rejecting requests for invalid tokens.
  • isInvalidToken : A method for determining whether a response from the server is an invalid token response.
  • refreshToken : Handles making calls to the server to get a new token.
  • storeToken : Handles saving the new token to our Vuex store.
  • retry : A method for retrying the original request from the user (after checking that token is valid or refreshing if needed).

OAuth

Work-in-progress (check back soon)...

Store Module

Just like each feature in our app has it's own Vuex store module, we can have a module for our Auth functionality as well:

src/auth/store.js

const defaults = {
  isLoggedIn: false,
  accessToken: null,
  refreshToken: null,
  user: {
    name: '',
    id: ''
  }
}

const auth = {
  namespaced: true,

  state: Object.assign({}, defaults),

  mutations: {
    update (state, data) {
      state = Object.assign({}, defaults, data)
    },
    clear (state) {
      state = Object.assign({}, defaults)
    }
  },

  actions: {
    clear ({ state, commit, rootState, dispatch }) {
      commit('clear')
    },
    update ({ state, commit, rootState }, data) {
      commit('update', data)
    }
  }
}

export default auth

Helpers

Note: I'll clean this code up soon. I added it fairly quickly and should soon have time to update it.

Let's create some helpers to aid us in performing things like http auth requests and logging in/out:

src/auth/helpers.js

import Vue from 'vue'
import { router } from '@/http'
import store from '@/store'
import auth from './'

const LOGIN_URL = '/auth'

// const CLIENT_SECRET = 'ZGVtb2FwcDpkZW1vcGFzcw==' // Base64(client_id:client_secret) "demoapp:demopass"

export default {
  URLSearchParams (obj) {
    var params = new URLSearchParams()

    for (var [key, value] of Object.entries(obj)) params.append(key, value)

    return params
  },

  login (creds, redirect, callback) {
    return Vue.http({
      method: 'post',
      url: LOGIN_URL,
      // headers: {
      //  'Authorization': 'Basic ' + CLIENT_SECRET,
      //  'Content-Type': 'application/x-www-form-urlencoded'
      // },
      data: this.URLSearchParams({
        grant_type: 'password',
        client_id: 'demoapp',
        client_secret: 'demopass',
        username: creds.username,
        password: creds.password
      })
    })
      .then((response) => {
        auth.storeToken(response)

        if (redirect) router.push({ name: redirect })
        return response
      })
      .catch((error) => {
        let errorMessage = null

        if (error.response) errorMessage = error.response.status
        else if (error.request) errorMessage = 'no response from server'
        else errorMessage = error.message

        return errorMessage
      })
  },

  logout () {
    store.dispatch('common/clear')
    router.push({ name: 'login' })
  },

  fakeLogin (creds, redirect) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        auth.storeToken({data: { accessToken: '123456789', refreshToken: '77777777' }})
        if (redirect) router.push({ name: redirect })
        resolve({})
      }, 500)
    })
  },

  // Standardizes errors. A place to add logging if needed.
  get (url, params = {}) {
    const config = {
      params: {
        username: store.state.auth.user.id,
        orgId: store.state.auth.user.orgId
      }
    }

    config.params = Object.assign(config.params, params)

    return Vue.auth.get(url, config)
      .then((response) => {
        return new Promise((resolve) => {
          // @TODO check for no response.data.data?
          resolve(response.data.data)
        })
      })
      .catch((error) => {
        // Standardize errors.
        let errorMessage = null

        if (error.response) {
          errorMessage = error.response.statusText || error.response.status
        } else if (error.request) {
          errorMessage = 'no response from server'
        } else {
          errorMessage = error.message
        }

        return new Promise((resolve, reject) => {
          reject(new Error(errorMessage))
        })
      })
  },

  put (url, data = {}) {
    const config = {}

    const defaultData = {
      username: store.state.auth.user.id,
      orgId: store.state.auth.user.orgId
    }

    data = Object.assign(defaultData, data)

    // console.log(settings.data)

    return Vue.auth.put(url, data, config)
      .then((response) => {
        if (response.data.errors) {
          return new Promise((resolve, reject) => {
            reject(new Error(response.data.errors[0].user_message))
          })
        }

        return new Promise((resolve) => {
          // @TODO check for no response.data.data?
          resolve(response.data.data)
        })
      })
      .catch((error) => {
        // Standardize errors.
        let errorMessage = null

        if (error.response) {
          errorMessage = error.response.statusText || error.response.status
        } else if (error.request) {
          errorMessage = 'no response from server'
        } else {
          errorMessage = error.message
        }

        return new Promise((resolve, reject) => {
          reject(new Error(errorMessage))
        })
      })
  }
}

Clone this wiki locally