Begin auth upgrades
moving to Auth0 universal login
This commit is contained in:
parent
948f64c295
commit
d95e2bf483
|
@ -57,6 +57,16 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
try {
|
||||
await this.$auth.renewTokens()
|
||||
} catch (e) {
|
||||
if (e !== 'Not logged in') {
|
||||
// eslint-disable-next-line
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.progress.events.$on('show', this.showProgress)
|
||||
this.progress.events.$on('done', this.hideProgress)
|
||||
|
@ -103,10 +113,10 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
// Custom theme
|
||||
@import "~vue-material/dist/theme/engine"
|
||||
@include md-register-theme("default", (primary: md-get-palette-color(green, 800), accent: md-get-palette-color(gray, 700)))
|
||||
@import "~vue-material/dist/theme/all"
|
||||
|
||||
html, body
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||
font-size: 1rem
|
||||
|
|
|
@ -1,19 +1,36 @@
|
|||
'use strict'
|
||||
|
||||
import auth0 from 'auth0-js'
|
||||
/* eslint-disable */
|
||||
import auth0 from 'auth0-js'
|
||||
import EventEmitter from 'events'
|
||||
|
||||
import AUTH_CONFIG from './auth0-variables'
|
||||
import mutations from '@/store/mutation-types'
|
||||
import mutations from '@/store/mutation-types'
|
||||
/* es-lint-enable*/
|
||||
|
||||
var tokenRenewalTimeout
|
||||
const webAuth = new auth0.WebAuth({
|
||||
domain: AUTH_CONFIG.domain,
|
||||
clientID: AUTH_CONFIG.clientId,
|
||||
redirectUri: AUTH_CONFIG.appDomain + AUTH_CONFIG.callbackUrl,
|
||||
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
|
||||
responseType: 'token id_token',
|
||||
scope: 'openid profile email'
|
||||
})
|
||||
|
||||
export default class AuthService {
|
||||
constructor () {
|
||||
this.login = this.login.bind(this)
|
||||
this.setSession = this.setSession.bind(this)
|
||||
this.logout = this.logout.bind(this)
|
||||
this.isAuthenticated = this.isAuthenticated.bind(this)
|
||||
const ACCESS_TOKEN = 'access_token'
|
||||
const ID_TOKEN = 'id_token'
|
||||
const EXPIRES_AT = 'expires_at'
|
||||
|
||||
class AuthService extends EventEmitter {
|
||||
|
||||
id = {
|
||||
token: null,
|
||||
expiry: null
|
||||
}
|
||||
access = {
|
||||
token: null,
|
||||
expiry: null
|
||||
}
|
||||
profile = null
|
||||
|
||||
auth0 = new auth0.WebAuth({
|
||||
domain: AUTH_CONFIG.domain,
|
||||
|
@ -24,8 +41,13 @@ export default class AuthService {
|
|||
scope: 'openid profile email'
|
||||
})
|
||||
|
||||
login () {
|
||||
this.auth0.authorize()
|
||||
/**
|
||||
* Starts the user log in flow
|
||||
*/
|
||||
login (customState) {
|
||||
webAuth.authorize({
|
||||
appState: customState
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +82,7 @@ export default class AuthService {
|
|||
})
|
||||
}
|
||||
|
||||
handleAuthentication (store, router) {
|
||||
handleAuthentication (store) {
|
||||
this.parseHash()
|
||||
.then(authResult => {
|
||||
if (authResult && authResult.accessToken && authResult.idToken) {
|
||||
|
@ -68,71 +90,89 @@ export default class AuthService {
|
|||
this.userInfo(authResult.accessToken)
|
||||
.then(user => {
|
||||
store.commit(mutations.USER_LOGGED_ON, user)
|
||||
router.replace('/journal')
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
router.replace('/')
|
||||
console.log(err)
|
||||
alert(`Error: ${err.error}. Check the console for further details.`)
|
||||
})
|
||||
}
|
||||
|
||||
scheduleRenewal () {
|
||||
let expiresAt = JSON.parse(localStorage.getItem('expires_at'))
|
||||
let delay = expiresAt - Date.now()
|
||||
if (delay > 0) {
|
||||
tokenRenewalTimeout = setTimeout(() => {
|
||||
this.renewToken()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
setSession (authResult) {
|
||||
// Set the time that the access token will expire at
|
||||
let expiresAt = JSON.stringify(
|
||||
authResult.expiresIn * 1000 + new Date().getTime()
|
||||
)
|
||||
localStorage.setItem('access_token', authResult.accessToken)
|
||||
localStorage.setItem('id_token', authResult.idToken)
|
||||
localStorage.setItem('expires_at', expiresAt)
|
||||
this.scheduleRenewal()
|
||||
this.id.token = authResult.idToken
|
||||
this.id.expiry = new Date(this.profile.exp * 1000);
|
||||
this.profile = authResult.idTokenPayload
|
||||
this.access.token = authResult.accessToken
|
||||
this.access.expiry = new Date(Date.now() + authResult.expiresIn * 1000)
|
||||
|
||||
localStorage.setItem(ACCESS_TOKEN, authResult.accessToken)
|
||||
localStorage.setItem(ID_TOKEN, authResult.idToken)
|
||||
localStorage.setItem(EXPIRES_AT, this.id.expiry)
|
||||
|
||||
this.emit('loginEvent', {
|
||||
loggedIn: true,
|
||||
profile: authResult.idTokenPayload,
|
||||
state: authResult.appState || {}
|
||||
})
|
||||
}
|
||||
|
||||
renewToken () {
|
||||
console.log('attempting renewal...')
|
||||
this.auth0.renewAuth(
|
||||
{
|
||||
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
|
||||
redirectUri: `${AUTH_CONFIG.appDomain}/static/silent.html`,
|
||||
usePostMessage: true
|
||||
},
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
this.setSession(result)
|
||||
}
|
||||
renewTokens () {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (localStorage.getItem(ID_TOKEN)) {
|
||||
webAuth.checkSession({}, (err, authResult) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
this.setSession(authResult)
|
||||
resolve(authResult)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject('Not logged in')
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
logout (store, router) {
|
||||
// Clear access token and ID token from local storage
|
||||
clearTimeout(tokenRenewalTimeout)
|
||||
localStorage.removeItem('access_token')
|
||||
localStorage.removeItem('id_token')
|
||||
localStorage.removeItem('expires_at')
|
||||
localStorage.setItem('user_profile', JSON.stringify({}))
|
||||
// navigate to the home route
|
||||
localStorage.removeItem(ACCESS_TOKEN)
|
||||
localStorage.removeItem(ID_TOKEN)
|
||||
localStorage.removeItem(EXPIRES_AT)
|
||||
|
||||
this.idToken = null
|
||||
this.idTokenExpiry = null
|
||||
this.profile = null
|
||||
|
||||
store.commit(mutations.USER_LOGGED_OFF)
|
||||
router.replace('/')
|
||||
|
||||
webAuth.logout({
|
||||
// navigate to the home route
|
||||
returnTo: '/'
|
||||
})
|
||||
this.emit('loginEvent', { loggedIn: false })
|
||||
}
|
||||
|
||||
isAuthenticated () {
|
||||
// Check whether the current time is past the access token's expiry time
|
||||
let expiresAt = JSON.parse(localStorage.getItem('expires_at'))
|
||||
return new Date().getTime() < expiresAt
|
||||
return Date().now() < this.id.Expiry && localStorage.getItem(ID_TOKEN)
|
||||
}
|
||||
|
||||
isAccessTokenValid () {
|
||||
return this.access.token && this.access.expiry && Date.now() < this.access.expiry
|
||||
}
|
||||
|
||||
getAccessToken () {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.isAccessTokenValid()) {
|
||||
resolve(this.access.token)
|
||||
} else {
|
||||
this.renewTokens()
|
||||
.then(authResult => {
|
||||
resolve(authResult.accessToken)
|
||||
}, reject)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService()
|
||||
|
|
|
@ -32,29 +32,28 @@
|
|||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import AuthService from '@/auth/AuthService'
|
||||
|
||||
export default {
|
||||
name: 'navigation',
|
||||
data () {
|
||||
return {
|
||||
auth0: new AuthService()
|
||||
}
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
isAuthenticated () {
|
||||
return this.$auth.isAuthenticated()
|
||||
},
|
||||
hasSnoozed () {
|
||||
return this.isAuthenticated &&
|
||||
Array.isArray(this.journal) &&
|
||||
this.journal.filter(req => req.snoozedUntil > Date.now()).length > 0
|
||||
},
|
||||
...mapState([ 'journal', 'isAuthenticated' ])
|
||||
...mapState([ 'journal' ])
|
||||
},
|
||||
methods: {
|
||||
logOn () {
|
||||
this.auth0.login()
|
||||
this.$auth.login()
|
||||
},
|
||||
logOff () {
|
||||
this.auth0.logout(this.$store, this.$router)
|
||||
this.$auth.logout(this.$store, this.$router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,17 @@ article.mpj-main-content(role='main')
|
|||
<script>
|
||||
'use strict'
|
||||
|
||||
import AuthService from '@/auth/AuthService'
|
||||
|
||||
export default {
|
||||
name: 'log-on',
|
||||
inject: ['progress'],
|
||||
created () {
|
||||
this.progress.$emit('show', 'indeterminate')
|
||||
new AuthService().handleAuthentication(this.$store, this.$router)
|
||||
// Auth service redirects to dashboard, which restarts the progress bar
|
||||
this.$auth.handleAuthentication(this.$store)
|
||||
},
|
||||
methods: {
|
||||
handleLoginEvent (data) {
|
||||
this.$router.push(data.state.target || '/journal')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -26,6 +26,7 @@ import router from './router'
|
|||
import store from './store'
|
||||
import DateFromNow from './components/common/DateFromNow'
|
||||
import PageTitle from './components/common/PageTitle'
|
||||
import AuthPlugin from './plugins/auth'
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
|
@ -52,6 +53,7 @@ Vue.use(MdTable)
|
|||
Vue.use(MdTabs)
|
||||
Vue.use(MdToolbar)
|
||||
Vue.use(MdTooltip)
|
||||
Vue.use(AuthPlugin)
|
||||
Vue.component('date-from-now', DateFromNow)
|
||||
Vue.component('page-title', PageTitle)
|
||||
|
||||
|
|
22
src/app/src/plugins/auth.js
Normal file
22
src/app/src/plugins/auth.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict'
|
||||
|
||||
import authService from '../auth/AuthService'
|
||||
|
||||
export default {
|
||||
install (Vue) {
|
||||
Vue.prototype.$auth = authService
|
||||
|
||||
Vue.mixin({
|
||||
created () {
|
||||
if (this.handleLoginEvent) {
|
||||
authService.addListener('loginEvent', this.handleLoginEvent)
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
if (this.handleLoginEvent) {
|
||||
authService.removeListener('loginEvent', this.handleLoginEvent)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
/* eslint-disable */
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Home from '@/components/Home'
|
||||
|
||||
import auth from './auth/AuthService'
|
||||
import Home from '@/components/Home'
|
||||
/* eslint-enable */
|
||||
|
||||
Vue.use(Router)
|
||||
|
@ -18,6 +20,12 @@ export default new Router({
|
|||
return { x: 0, y: 0 }
|
||||
}
|
||||
},
|
||||
beforeEach (to, from, next) {
|
||||
if (to.path === '/' || to.path === '/user/log-on' || auth.isAuthenticated()) {
|
||||
return next()
|
||||
}
|
||||
auth.login({ target: to.path })
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
|
|
Loading…
Reference in New Issue
Block a user