Permalink
Browse files

feat: use new Auth class

BREAKING CHANGE: Lot's of API and Usage changes
  • Loading branch information...
pi0 committed Feb 1, 2018
1 parent c44c91c commit d4da740f34d7a5795aa081e3d3f75c53cdbc7cd2
Showing with 590 additions and 300 deletions.
  1. +1 βˆ’1 README.md
  2. +290 βˆ’0 lib/auth.js
  3. +44 βˆ’0 lib/auth.plugin.js
  4. +15 βˆ’24 lib/defaults.js
  5. +25 βˆ’13 lib/module.js
  6. +0 βˆ’46 lib/templates/auth.middleware.js
  7. +0 βˆ’21 lib/templates/auth.plugin.js
  8. +0 βˆ’182 lib/templates/auth.store.js
  9. +7 βˆ’1 package.json
  10. +59 βˆ’0 test/fixture/api/auth.js
  11. +4 βˆ’1 test/fixture/nuxt.config.js
  12. +32 βˆ’5 test/fixture/pages/index.vue
  13. +113 βˆ’6 yarn.lock
@@ -47,7 +47,7 @@ Edit `nuxt.config.js`:
```js
{
modules: [
'@nuxtjs/axios', // <-- Should be before @nuxtjs/auth
'@nuxtjs/axios',
'@nuxtjs/auth'
],
@@ -0,0 +1,290 @@
import Cookie from 'cookie'
import Cookies from 'js-cookie'
import Vue from 'vue'
import Hookable from 'hable'
export default class Auth extends Hookable {
constructor (ctx, options) {
super()
this.ctx = ctx
this.app = ctx.app
this.options = options
// Keep token out of the store for security reasons
Vue.set(this, 'token', null)
// Reset on error
if (this.options.resetOnError) {
this._resetOnError()
}
this._registerVuexStore()
}
_registerVuexStore () {
const authModule = {
namespaced: true,
state: () => ({
user: null,
loggedIn: false
}),
mutations: {
SET (state, payload) {
Vue.set(state, payload.key, payload.value)
}
}
}
this.$store.registerModule(this.options.namespace, authModule, {
preserveState: Boolean(this.$store.state[this.options.namespace])
})
}
_resetOnError () {
this.hook('error', () => {
this.reset()
})
}
_watchLoggedIn () {
return this.$store.watch(
() => this.$store.stat[this.options.namespace + '/loggedIn'],
newAuthState => {
if (newAuthState) {
this.redirectToHome()
} else {
this.redirectToLogin()
}
}
)
}
get $axios () {
if (!this.app.$axios) {
throw new Error('$axios is not available')
}
return this.app.$axios
}
get $store () {
return this.ctx.store
}
get $req () {
return this.app.context.req
}
get $res () {
return this.app.context.res
}
get isAPIRequest () {
return (
process.server &&
this.$req.url.indexOf(this.options.endpoints.user.url) === 0
)
}
get state () {
return this.$store.state[this.options.namespace]
}
reset () {
this.setState('loggedIn', false)
this.setState('token', null)
this.setState('user', null)
if (this.options.cookie) {
this.setCookie(this.options.cookie.name, null)
}
if (this.options.token.localStorage) {
this.setLocalStorage(this.options.token.name, null)
}
}
setState (key, value) {
if (key === 'token') {
this.token = value
return
}
this.$store.commit(this.options.namespace + '/SET', { key, value })
}
getState (key) {
if (key === 'token') {
return this.token
}
return this.state[key]
}
setLocalStorage (name, value) {
if (typeof localStorage !== 'undefined') {
if (value) {
localStorage.setItem(name, value)
} else {
localStorage.removeItem(name)
}
}
}
getLocalStorage (name) {
if (typeof localStorage !== 'undefined') {
return localStorage.getItem(name)
}
}
setCookie (name, value, params = {}) {
if (!this.options.cookie) {
return
}
const _params = Object.assign({}, this.options.cookie.params, params)
if (!value) {
let date = new Date()
date.setDate(date.getDate() - 1)
_params.expires = date
}
if (process.browser) {
Cookies.set(name, value, _params)
} else {
// Don't send duplicate token via Set-Cookie
if (!value) {
this.$res.setHeader(
'Set-Cookie',
Cookie.serialize(name, value, _params)
)
}
}
}
getCookie (name) {
const cookieStr = process.browser
? document.cookie
: this.$req.headers.cookie
const cookies = Cookie.parse(cookieStr || '') || {}
return cookies[name]
}
async _fetch (name, endpoint) {
const defaults = this.options.endpoints[name]
if (!defaults) {
return
}
try {
const { data } = await this.$axios.request(
Object.assign({}, defaults, endpoint)
)
return data
} catch (error) {
await this.callHook('error', { name, endpoint, error })
}
}
async login (endpoint) {
const data = await this._fetch('login', endpoint)
if (!data) {
return
}
// Extract and set token
this.setToken(data.token)
// Fetch User
if (this.options.fetchUserOnLogin) {
return this.fetchUser()
}
// Set loggedIn to true
this.setState('loggedIn', true)
}
async fetchUser (endpoint) {
if (this.options.token && !this.getState('token')) {
return
}
const data = await this._fetch('user', endpoint)
if (!data) {
return
}
this.setState('user', data.user)
this.setState('loggedIn', true)
}
async logout (endpoint) {
await this._fetch('logout', endpoint)
this.reset()
}
setToken (token) {
if (!this.options.token) {
return
}
// Update local state
this.setState('token', token)
// Set Authorization token for all axios requests
this.$axios.setToken(token, this.options.token.type)
// Save it in cookies
if (this.options.cookie) {
this.setCookie(this.options.cookie.name, token)
}
// Save it in localSotage
if (this.options.token.localStorage) {
this.setLocalStorage(this.options.token.name, token)
}
}
syncToken () {
if (!this.options.token) {
return
}
let token = this.getState('token')
if (!token && this.options.cookie) {
token = this.getCookie(this.options.cookie.name)
}
if (!token && this.options.token.localStorage) {
token = this.getLocalStorage(this.options.token.name)
}
this.setToken(token)
}
redirect () {
if (this.getState('loggedIn')) {
this.redirectToHome()
} else {
this.redirectToLogin()
}
}
redirectToLogin () {
if (this.options.redirect.login) {
this.ctx.redirect(this.options.redirect.login)
}
}
redirectToHome () {
if (this.options.redirect.home) {
this.ctx.redirect(this.options.redirect.home)
}
}
}
@@ -0,0 +1,44 @@
import Auth from './auth'
import Middleware from './middleware'
export default function (ctx, inject) {
// Create new Auth instance
const $auth = new Auth(ctx, <%= JSON.stringify(options, undefined, 2).replace(/"/g,'\'') %>)
// Prevent infinity redirects
if ($auth.isAPIRequest) {
return
}
// Inject it to nuxt context as $auth
inject('auth', $auth)
// Sync token
$auth.syncToken()
// Fetch user if is not available
if (!$auth.state.user) {
return $auth.fetchUser()
}
}
// Register auth middleware
Middleware.auth = function (ctx) {
if (!routeOption(ctx.oute, 'noRedirect')) {
ctx.app.$auth.redirect()
}
}
// Utility to get route option
function routeOption (route, key) {
return route.matched.some(m => {
// Browser
if (process.browser) {
return Object.values(m.components).some(component => component.options[key])
}
// SSR
return Object.values(m.components).some(component =>
Object.values(component._Ctor).some(ctor => ctor.options && ctor.options[key])
)
})
}
@@ -1,34 +1,25 @@
module.exports = {
user: {
endpoint: '/api/auth/user',
propertyName: 'user',
resetOnFail: true,
enabled: true,
method: 'GET'
},
login: {
endpoint: '/api/auth/login'
},
logout: {
endpoint: '/api/auth/logout',
method: 'GET'
fetchUserOnLogin: true,
resetOnError: true,
namespace: 'auth',
endpoints: {
login: { url: '/api/auth/login', method: 'post', propertyName: 'token' },
logout: { url: '/api/auth/logout', method: 'post' },
user: { url: '/api/auth/user', method: 'get', propertyName: 'user' }
},
redirect: {
guest: true,
user: true,
notLoggedIn: '/login',
loggedIn: '/'
login: '/login',
home: '/'
},
token: {
enabled: true,
type: 'Bearer',
localStorage: true,
name: 'token',
cookie: true,
cookieName: 'token'
localStorage: true
},
errorHandler: {
fetch: null,
logout: null
cookie: {
name: 'token',
params: {
path: '/'
}
}
}
Oops, something went wrong.

0 comments on commit d4da740

Please sign in to comment.