From a9e8bfdaa30353adb71d3d0a43e12f2f06eb947d Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 5 Nov 2018 23:36:46 +0000 Subject: [PATCH] --- app/ayanova/package-lock.json | 5 ++ app/ayanova/package.json | 3 +- app/ayanova/src/router.js | 63 +++++++++---------- app/ayanova/src/store.js | 8 +-- app/ayanova/src/utils/auth.js | 104 ++++++++++++++++++++++++++++++++ app/ayanova/src/views/login.vue | 71 +++++++++++----------- devdocs/todo.txt | 8 +++ devdocs/tools.txt | 17 ++++++ 8 files changed, 209 insertions(+), 70 deletions(-) create mode 100644 app/ayanova/src/utils/auth.js diff --git a/app/ayanova/package-lock.json b/app/ayanova/package-lock.json index 337a82af..93ff9b8c 100644 --- a/app/ayanova/package-lock.json +++ b/app/ayanova/package-lock.json @@ -9729,6 +9729,11 @@ "verror": "1.10.0" } }, + "jwt-decode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", + "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", diff --git a/app/ayanova/package.json b/app/ayanova/package.json index ad8eaebe..f6267655 100644 --- a/app/ayanova/package.json +++ b/app/ayanova/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@babel/polyfill": "^7.0.0-rc.1", + "jwt-decode": "^2.2.0", "register-service-worker": "^1.0.0", "vue": "^2.5.17", "vue-router": "^3.0.1", @@ -84,4 +85,4 @@ ], "testURL": "http://localhost/" } -} \ No newline at end of file +} diff --git a/app/ayanova/src/router.js b/app/ayanova/src/router.js index 5230aa5c..bdb771bc 100644 --- a/app/ayanova/src/router.js +++ b/app/ayanova/src/router.js @@ -1,6 +1,7 @@ import Vue from "vue"; import Router from "vue-router"; import Home from "./views/Home.vue"; +import { isLoggedIn, login, logout } from './utils/auth'; Vue.use(Router); @@ -44,34 +45,34 @@ export default new Router({ ] }); -// router.beforeEach((to, from, next) => { -// if(to.matched.some(record => record.meta.requiresAuth)) { -// if (localStorage.getItem('jwt') == null) { -// next({ -// path: '/login', -// params: { nextUrl: to.fullPath } -// }) -// } else { -// let user = JSON.parse(localStorage.getItem('user')) -// if(to.matched.some(record => record.meta.is_admin)) { -// if(user.is_admin == 1){ -// next() -// } -// else{ -// next({ name: 'userboard'}) -// } -// }else { -// next() -// } -// } -// } else if(to.matched.some(record => record.meta.guest)) { -// if(localStorage.getItem('jwt') == null){ -// next() -// } -// else{ -// next({ name: 'userboard'}) -// } -// }else { -// next() -// } -// }) +router.beforeEach((to, from, next) => { + if(to.matched.some(record => record.meta.requiresAuth)) { + if (localStorage.getItem('jwt') == null) { + next({ + path: '/login', + params: { nextUrl: to.fullPath } + }) + } else { + let user = JSON.parse(localStorage.getItem('user')) + if(to.matched.some(record => record.meta.is_admin)) { + if(user.is_admin == 1){ + next() + } + else{ + next({ name: 'userboard'}) + } + }else { + next() + } + } + } else if(to.matched.some(record => record.meta.guest)) { + if(localStorage.getItem('jwt') == null){ + next() + } + else{ + next({ name: 'userboard'}) + } + }else { + next() + } +}) diff --git a/app/ayanova/src/store.js b/app/ayanova/src/store.js index ceedaf67..a4fe7435 100644 --- a/app/ayanova/src/store.js +++ b/app/ayanova/src/store.js @@ -6,10 +6,10 @@ Vue.use(Vuex); export default new Vuex.Store({ state: { authenticated: false, - mockAccount: { - username: "manager", - password: "letmein" - } + mockAccount: { + username: "manager", + password: "letmein" + } }, mutations: {}, actions: {} diff --git a/app/ayanova/src/utils/auth.js b/app/ayanova/src/utils/auth.js new file mode 100644 index 00000000..1cfdad35 --- /dev/null +++ b/app/ayanova/src/utils/auth.js @@ -0,0 +1,104 @@ +import decode from "jwt-decode"; +//import axios from 'axios'; +//import auth0 from 'auth0-js'; +//import Router from 'vue-router'; +//import Auth0Lock from 'auth0-lock'; +const ID_TOKEN_KEY = "id_token"; +const ACCESS_TOKEN_KEY = "access_token"; + +// const CLIENT_ID = '{AUTH0_CLIENT_ID}'; +// const CLIENT_DOMAIN = '{AUTH0_DOMAIN}'; +// const REDIRECT = 'YOUR_CALLBACK_URL'; +// const SCOPE = '{SCOPE}'; +// const AUDIENCE = 'AUDIENCE_ATTRIBUTE'; + +// var auth = new auth0.WebAuth({ +// clientID: CLIENT_ID, +// domain: CLIENT_DOMAIN +// }); + +export function login() { + // auth.authorize({ + // responseType: 'token id_token', + // redirectUri: REDIRECT, + // audience: AUDIENCE, + // scope: SCOPE + // }); +} + +// var router = new Router({ +// mode: 'history', +// }); + +export function logout() { + clearIdToken(); + clearAccessToken(); + //router.go('/'); +} + +// export function requireAuth(to, from, next) { +// if (!isLoggedIn()) { +// next({ +// path: '/', +// query: { redirect: to.fullPath } +// }); +// } else { +// next(); +// } +// } + +export function getIdToken() { + return localStorage.getItem(ID_TOKEN_KEY); +} + +export function getAccessToken() { + return localStorage.getItem(ACCESS_TOKEN_KEY); +} + +function clearIdToken() { + localStorage.removeItem(ID_TOKEN_KEY); +} + +function clearAccessToken() { + localStorage.removeItem(ACCESS_TOKEN_KEY); +} + +// Helper function that will allow us to extract the access_token and id_token +function getParameterByName(name) { + let match = RegExp("[#&]" + name + "=([^&]*)").exec(window.location.hash); + return match && decodeURIComponent(match[1].replace(/\+/g, " ")); +} + +// Get and store access_token in local storage +export function setAccessToken() { + let accessToken = getParameterByName("access_token"); + localStorage.setItem(ACCESS_TOKEN_KEY, accessToken); +} + +// Get and store id_token in local storage +export function setIdToken() { + let idToken = getParameterByName("id_token"); + localStorage.setItem(ID_TOKEN_KEY, idToken); +} + +export function isLoggedIn() { + const idToken = getIdToken(); + return !!idToken && !isTokenExpired(idToken); +} + +function getTokenExpirationDate(encodedToken) { + const token = decode(encodedToken); + if (!token.exp) { + return null; + } + + const date = new Date(0); + date.setUTCSeconds(token.exp); + + return date; +} + +function isTokenExpired(token) { + const expirationDate = getTokenExpirationDate(token); + return expirationDate < new Date(); +} diff --git a/app/ayanova/src/views/login.vue b/app/ayanova/src/views/login.vue index 34266f9f..3124a8c4 100644 --- a/app/ayanova/src/views/login.vue +++ b/app/ayanova/src/views/login.vue @@ -1,47 +1,50 @@ \ No newline at end of file +#login { + width: 500px; + border: 1px solid #cccccc; + background-color: #ffffff; + margin: auto; + margin-top: 200px; + padding: 20px; +} + diff --git a/devdocs/todo.txt b/devdocs/todo.txt index a3e44ac1..a23362f5 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -65,6 +65,14 @@ WEEK OF 2018-11-05 - RAVEN shell start work. YAY! - Reporting - Notifications from the server - File upload / download + - Offline abilities? + - Need a good think about this, time to market is of utmost importance but it would not hurt to see what can be done now or defferred to vNext + - v7 is technically an always online app so there's that. + - Would be nice to maybe be able to do some limited stuff offline + - scope out a scenario or leave for vNext? + - Fill out a workorder that is already up on the phone? + - Parts can't be cached locally, there could be thousands, but maybe labour only is supported? + - Maybe parts can be entered as text when offline for later lookup? - NOTE: many users want to see some progress on v8 so it's important that the initial shell have some "flash" to it - Nice transitions - Some sample charts diff --git a/devdocs/tools.txt b/devdocs/tools.txt index efd154a6..8ca9c1e3 100644 --- a/devdocs/tools.txt +++ b/devdocs/tools.txt @@ -71,6 +71,23 @@ and VUEX allows that easily, plus it's fairly straightforward to use. Here is an example of how to structure a non trivial application with VUEX broken into modules: https://vuex.vuejs.org/guide/structure.html +VUEX STATE PERSISTENCE = VUEX-PERSISTEDSTATE +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + - https://github.com/robinvdvleuten/vuex-persistedstate + - Some stuff should probably be persisted like auth, maybe other stuff? + - Is this app an online only app? + - Store enough stuff to keep working locally? + - Fill out a workorder offline? + - Create a new one offline? + - enter labour only, no parts or other lengthy lookup stuff?? + - If recreating AyaNova 7 essentially, then that app is always online as well so maybe don't even consider offline work? + + +LOCALSTORAGE = STORE.JS +=-=-=-=-=-=-=-=-=-=- + - Very widely used and I don't necessarily need a VUE specific one, this is the one used for pecklist and rockfish + - https://github.com/marcuswestin/store.js/ + SERVICEWORKER = WORKBOX =-=-=-=-=-=-=-=-=-=-=-= For offsite PWA use, works well and is simple to implement