myPrayerJournal v2 #27

Merged
danieljsummers merged 27 commits from version-2 into master 2019-09-03 00:01:26 +00:00
7 changed files with 155 additions and 72 deletions
Showing only changes of commit d95e2bf483 - Show all commits

View File

@ -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

View File

@ -1,19 +1,36 @@
'use strict'
/* eslint-disable */
import auth0 from 'auth0-js'
import EventEmitter from 'events'
import AUTH_CONFIG from './auth0-variables'
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) => {
renewTokens () {
return new Promise((resolve, reject) => {
if (localStorage.getItem(ID_TOKEN)) {
webAuth.checkSession({}, (err, authResult) => {
if (err) {
console.log(err)
reject(err)
} else {
this.setSession(result)
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()

View File

@ -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)
}
}
}

View File

@ -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>

View File

@ -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)

View 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)
}
}
})
}
}

View File

@ -3,6 +3,8 @@
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import auth from './auth/AuthService'
import Home from '@/components/Home'
/* eslint-enable */
@ -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: '/',