myPrayerJournal v2 #27

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

View File

@ -72,7 +72,8 @@ module Configure =
fun opts ->
let jwtCfg = cfg.GetSection "Auth0"
opts.Authority <- sprintf "https://%s/" jwtCfg.["Domain"]
opts.Audience <- jwtCfg.["Id"])
opts.Audience <- jwtCfg.["Id"]
)
|> ignore
sc.AddSingleton<IJsonSerializer> (NewtonsoftJsonSerializer jsonSettings)
|> ignore

View File

@ -35,6 +35,7 @@ import Vue from 'vue'
import Navigation from '@/components/common/Navigation'
import actions from '@/store/action-types'
import { version } from '../package.json'
export default {
@ -57,21 +58,12 @@ export default {
}
}
},
async created () {
try {
await this.$auth.renewTokens()
} catch (e) {
if (e !== 'Not logged in') {
// eslint-disable-next-line
console.log(e)
}
}
},
mounted () {
async mounted () {
this.progress.events.$on('show', this.showProgress)
this.progress.events.$on('done', this.hideProgress)
this.snackbar.events.$on('info', this.showInfo)
this.snackbar.events.$on('error', this.showError)
await this.$store.dispatch(actions.CHECK_AUTHENTICATION)
},
computed: {
version () {

View File

@ -15,12 +15,12 @@ export default {
* Set the bearer token for all future requests
* @param {string} token The token to use to identify the user to the server
*/
setBearer: token => { http.defaults.headers.common['authorization'] = `Bearer ${token}` },
setBearer: token => { http.defaults.headers.common['Authorization'] = `Bearer ${token}` },
/**
* Remove the bearer token
*/
removeBearer: () => delete http.defaults.headers.common['authorization'],
removeBearer: () => delete http.defaults.headers.common['Authorization'],
/**
* Add a note for a prayer request

View File

@ -11,15 +11,10 @@ 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'
})
const ACCESS_TOKEN = 'access_token'
const ID_TOKEN = 'id_token'
const EXPIRES_AT = 'expires_at'
class AuthService extends EventEmitter {
id = {
@ -32,14 +27,10 @@ class AuthService extends EventEmitter {
}
profile = null
auth0 = 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'
})
ACCESS_TOKEN = 'access_token'
ID_TOKEN = 'id_token'
EXPIRES_AT = 'expires_at'
USER_PROFILE = 'user_profile'
/**
* Starts the user log in flow
@ -55,7 +46,7 @@ class AuthService extends EventEmitter {
*/
parseHash () {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
webAuth.parseHash((err, authResult) => {
if (err) {
reject(err)
} else {
@ -65,32 +56,12 @@ class AuthService extends EventEmitter {
})
}
/**
* Promisified userInfo function
*
* @param token The auth token from the login result
*/
userInfo (token) {
return new Promise((resolve, reject) => {
this.auth0.client.userInfo(token, (err, user) => {
if (err) {
reject(err)
} else {
resolve(user)
}
})
})
}
handleAuthentication (store) {
this.parseHash()
.then(authResult => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
this.userInfo(authResult.accessToken)
.then(user => {
store.commit(mutations.USER_LOGGED_ON, user)
})
store.commit(mutations.USER_LOGGED_ON, this.profile)
}
})
.catch(err => {
@ -100,15 +71,16 @@ class AuthService extends EventEmitter {
}
setSession (authResult) {
this.profile = authResult.idTokenPayload
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)
localStorage.setItem(this.ACCESS_TOKEN, authResult.accessToken)
localStorage.setItem(this.ID_TOKEN, authResult.idToken)
localStorage.setItem(this.EXPIRES_AT, this.id.expiry)
localStorage.setItem(this.USER_PROFILE, JSON.stringify(this.profile))
this.emit('loginEvent', {
loggedIn: true,
@ -119,7 +91,7 @@ class AuthService extends EventEmitter {
renewTokens () {
return new Promise((resolve, reject) => {
if (localStorage.getItem(ID_TOKEN)) {
if (localStorage.getItem(this.ID_TOKEN) !== null) {
webAuth.checkSession({}, (err, authResult) => {
if (err) {
reject(err)
@ -136,9 +108,10 @@ class AuthService extends EventEmitter {
logout (store, router) {
// Clear access token and ID token from local storage
localStorage.removeItem(ACCESS_TOKEN)
localStorage.removeItem(ID_TOKEN)
localStorage.removeItem(EXPIRES_AT)
localStorage.removeItem(this.ACCESS_TOKEN)
localStorage.removeItem(this.ID_TOKEN)
localStorage.removeItem(this.EXPIRES_AT)
localStorage.removeItem(this.USER_PROFILE)
this.idToken = null
this.idTokenExpiry = null
@ -154,7 +127,7 @@ class AuthService extends EventEmitter {
}
isAuthenticated () {
return Date().now() < this.id.Expiry && localStorage.getItem(ID_TOKEN)
return Date.now() < this.id.Expiry && localStorage.getItem(this.ID_TOKEN)
}
isAccessTokenValid () {

View File

@ -38,15 +38,12 @@ export default {
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' ])
...mapState([ 'isAuthenticated', 'journal' ])
},
methods: {
logOn () {

View File

@ -3,6 +3,8 @@
export default {
/** Action to add a prayer request (pass request text) */
ADD_REQUEST: 'add-request',
/** Action to check if a user is authenticated, refreshing the session first if it exists */
CHECK_AUTHENTICATION: 'check-authentication',
/** Action to load the user's prayer journal */
LOAD_JOURNAL: 'load-journal',
/** Action to update a request */

View File

@ -4,8 +4,8 @@
import Vue from 'vue'
import Vuex from 'vuex'
import api from '@/api'
import AuthService from '@/auth/AuthService'
import api from '@/api'
import auth from '@/auth/AuthService'
import mutations from './mutation-types'
import actions from './action-types'
@ -13,37 +13,45 @@ import actions from './action-types'
Vue.use(Vuex)
const auth0 = new AuthService()
const logError = function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
console.error(error.response.data)
console.error(error.response.status)
console.error(error.response.headers)
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request)
console.error(error.request)
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message)
console.error('Error', error.message)
}
console.error(`config: ${error.config}`)
}
/**
* Set the "Bearer" authorization header with the current access token
*/
const setBearer = async function () {
try {
await auth.getAccessToken()
api.setBearer(localStorage.getItem(auth.ID_TOKEN))
} catch(err) {
if (err === 'Not logged in') {
console.warn('API request attempted when user was not logged in')
} else {
console.error(err)
}
}
console.log(error.config)
}
export default new Vuex.Store({
state: {
user: JSON.parse(localStorage.getItem('user_profile') || '{}'),
isAuthenticated: (() => {
auth0.scheduleRenewal()
if (auth0.isAuthenticated()) {
api.setBearer(localStorage.getItem('id_token'))
}
return auth0.isAuthenticated()
})(),
user: JSON.parse(localStorage.getItem(auth.USER_PROFILE) || '{}'),
isAuthenticated: auth.isAuthenticated(),
journal: {},
isLoadingJournal: false
},
@ -62,15 +70,16 @@ export default new Vuex.Store({
if (request.lastStatus !== 'Answered') jrnl.push(request)
state.journal = jrnl
},
[mutations.SET_AUTHENTICATION] (state, value) {
state.isAuthenticated = value
},
[mutations.USER_LOGGED_OFF] (state) {
state.user = {}
api.removeBearer()
state.isAuthenticated = false
},
[mutations.USER_LOGGED_ON] (state, user) {
localStorage.setItem('user_profile', JSON.stringify(user))
state.user = user
api.setBearer(localStorage.getItem('id_token'))
state.isAuthenticated = true
}
},
@ -78,6 +87,7 @@ export default new Vuex.Store({
async [actions.ADD_REQUEST] ({ commit }, { progress, requestText, recurType, recurCount }) {
progress.$emit('show', 'indeterminate')
try {
await setBearer()
const newRequest = await api.addRequest(requestText, recurType, recurCount)
commit(mutations.REQUEST_ADDED, newRequest.data)
progress.$emit('done')
@ -86,11 +96,19 @@ export default new Vuex.Store({
progress.$emit('done')
}
},
async [actions.CHECK_AUTHENTICATION] ({ commit }) {
try {
await auth.renewTokens()
commit(mutations.SET_AUTHENTICATION, auth.isAuthenticated())
} catch(_) {
commit(mutations.SET_AUTHENTICATION, false)
}
},
async [actions.LOAD_JOURNAL] ({ commit }, progress) {
commit(mutations.LOADED_JOURNAL, {})
progress.$emit('show', 'query')
commit(mutations.LOADING_JOURNAL, true)
api.setBearer(localStorage.getItem('id_token'))
await setBearer()
try {
const jrnl = await api.journal()
commit(mutations.LOADED_JOURNAL, jrnl.data)
@ -105,6 +123,7 @@ export default new Vuex.Store({
async [actions.UPDATE_REQUEST] ({ commit, state }, { progress, requestId, status, updateText, recurType, recurCount }) {
progress.$emit('show', 'indeterminate')
try {
await setBearer()
let oldReq = (state.journal.filter(req => req.requestId === requestId) || [])[0] || {}
if (!(status === 'Prayed' && updateText === '')) {
if (status !== 'Answered' && (oldReq.recurType !== recurType || oldReq.recurCount !== recurCount)) {
@ -125,6 +144,7 @@ export default new Vuex.Store({
async [actions.SHOW_REQUEST_NOW] ({ commit }, { progress, requestId, showAfter }) {
progress.$emit('show', 'indeterminate')
try {
await setBearer()
await api.showRequest(requestId, showAfter)
const request = await api.getRequest(requestId)
commit(mutations.REQUEST_UPDATED, request.data)
@ -137,6 +157,7 @@ export default new Vuex.Store({
async [actions.SNOOZE_REQUEST] ({ commit }, { progress, requestId, until }) {
progress.$emit('show', 'indeterminate')
try {
await setBearer()
await api.snoozeRequest(requestId, until)
const request = await api.getRequest(requestId)
commit(mutations.REQUEST_UPDATED, request.data)

View File

@ -9,6 +9,8 @@ export default {
REQUEST_ADDED: 'request-added',
/** Mutation to replace a prayer request at the top of the current journal */
REQUEST_UPDATED: 'request-updated',
/** Mutation for setting the authentication state */
SET_AUTHENTICATION: 'set-authentication',
/** Mutation for logging a user off */
USER_LOGGED_OFF: 'user-logged-off',
/** Mutation for logging a user on (pass user) */