From a323cf862aebf760964bdf2384da6b59bf7e27f0 Mon Sep 17 00:00:00 2001 From: Sockenklaus Date: Fri, 22 Oct 2021 05:28:51 +0200 Subject: [PATCH] implemented token based login --- package-lock.json | 59 +++++++++++++++++++++++++++ package.json | 1 + src/App.vue | 17 +++++++- src/components/HelloWorld.vue | 52 ----------------------- src/main.ts | 6 ++- src/router/index.ts | 25 ++++++++++-- src/stores/user.ts | 77 +++++++++++++++++++++++++++++++++-- src/views/Login.vue | 15 +++---- 8 files changed, 179 insertions(+), 73 deletions(-) delete mode 100644 src/components/HelloWorld.vue diff --git a/package-lock.json b/package-lock.json index 5a6faba..38d2308 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "bootstrap-icons": "^1.6.0", "date-fns": "^2.25.0", "pinia": "^2.0.0-rc.13", + "pinia-plugin-persist": "^0.0.92", "vue": "^3.2.20", "vue-router": "^4.0.12", "vue-tsc": "^0.3.0" @@ -1125,6 +1126,48 @@ } } }, + "node_modules/pinia-plugin-persist": { + "version": "0.0.92", + "resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-0.0.92.tgz", + "integrity": "sha512-eBNv1mqWwtiRPg4lraHuhXTqsPt51tRT2yaukW8+Gj9NIbLuPKS/VhnwmTF46WpIMJ0OP5xC4cwz9JOKJGX3GQ==", + "dependencies": { + "vue-demi": "^0.11.4" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/pinia-plugin-persist/node_modules/vue-demi": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.11.4.tgz", + "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pinia/node_modules/vue-demi": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.11.4.tgz", @@ -2478,6 +2521,22 @@ } } }, + "pinia-plugin-persist": { + "version": "0.0.92", + "resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-0.0.92.tgz", + "integrity": "sha512-eBNv1mqWwtiRPg4lraHuhXTqsPt51tRT2yaukW8+Gj9NIbLuPKS/VhnwmTF46WpIMJ0OP5xC4cwz9JOKJGX3GQ==", + "requires": { + "vue-demi": "^0.11.4" + }, + "dependencies": { + "vue-demi": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.11.4.tgz", + "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==", + "requires": {} + } + } + }, "postcss": { "version": "8.3.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.9.tgz", diff --git a/package.json b/package.json index 5594e6e..acd3e2e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "bootstrap-icons": "^1.6.0", "date-fns": "^2.25.0", "pinia": "^2.0.0-rc.13", + "pinia-plugin-persist": "^0.0.92", "vue": "^3.2.20", "vue-router": "^4.0.12", "vue-tsc": "^0.3.0" diff --git a/src/App.vue b/src/App.vue index 4bed711..9004aca 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,9 +4,10 @@
@@ -18,6 +19,18 @@ diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index 2d61249..0000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/src/main.ts b/src/main.ts index 7c492c0..47e8432 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' +import piniaPersist from 'pinia-plugin-persist' import App from './App.vue' import router from './router' import Oruga from '@oruga-ui/oruga-next' @@ -8,8 +9,11 @@ import 'bootstrap' import "bootstrap/scss/bootstrap.scss" import "bootstrap-icons/font/bootstrap-icons.css" +const pinia = createPinia() +pinia.use(piniaPersist) + createApp(App) .use(router) .use(Oruga) -.use(createPinia()) +.use(pinia) .mount('#app') diff --git a/src/router/index.ts b/src/router/index.ts index 5add4c3..a10df9e 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,17 +1,29 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' -import Home from '../views/Home.vue' -import Login from '../views/Login.vue' +import { useUser } from '@/stores/user' const routes: Array = [ { path: '/', name: 'Home', - component: Home + component: () => import('@/views/Home.vue'), + meta: { + requiresAuth: true, + requiresAdmin: false + } }, { path: '/login', name: 'Login', - component: Login + component: () => import('@/views/Login.vue'), + meta: { + requiresAuth: false, + requiresAdmin: false + } + }, + { + path: '/:pathMatch(.*)*/', + name: 'not-found', + redirect: '/login' } ] @@ -20,4 +32,9 @@ const router = createRouter({ routes }) +router.beforeEach((to, from) => { + if (to.meta.requiresAuth && !useUser().isLoggedIn) return '/login' + if (to.meta.requiresAdmin && !useUser().isAdmin) return false +}) + export default router diff --git a/src/stores/user.ts b/src/stores/user.ts index 736e419..d47d866 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -10,7 +10,8 @@ type AuthSuccResult = { text: string } user: string, - role: string + role: string, + token: string } type AuthErrResult = { @@ -20,17 +21,34 @@ type AuthErrResult = { } } -export const useUser = defineStore('userStore', { + + +export const useUser = defineStore({ + id: 'storeUser', + state: () => { return { user: '', role: '', - isLoggedIn: false + isLoggedIn: false, + rememberMe: false, + token: '' + } + }, + + getters: { + isAdmin: (state) => state.role === 'admin', + + preferredStorage: (state) => { + return localStorage + /* if (state.rememberMe) return localStorage + else return sessionStorage */ } }, actions: { - async login(username: string, password: string) : Promise { + async login(username: string, password: string, rememberMe: boolean): Promise { + const { notifications } = storeToRefs(useNotifications()) try { @@ -38,10 +56,13 @@ export const useUser = defineStore('userStore', { username: username, password: password }) + + console.log(response) this.isLoggedIn = true this.user = response.data.user this.role = response.data.role + this.token = response.data.token notifications.value.push({ type: response.data.notification.type, @@ -67,6 +88,54 @@ export const useUser = defineStore('userStore', { return false } + }, + + async logout(): Promise { + + const { notifications } = storeToRefs(useNotifications()) + + try { + const ai = axios.create({ + baseURL: 'http://localhost:3333/api/v1/', + headers: { + 'Authorization': 'Bearer '+this.token + } + + }) + await ai.post('/logout') + + notifications.value.push({ + type: 'success', + text: 'Logged out successfully', + randomKey: getUnixTime(new Date()).toString() + }) + + this.$reset() + + return true + } + catch(error) { + if(error instanceof Error) { + const notification = { + type: 'danger', + text: error.message, + randomKey: getUnixTime(new Date()).toString() + /**TODO #19 Generate randomKey in notification-store! */ + } + + notifications.value.push(notification) + } + return false + } } + }, + + persist: { + enabled: true, + strategies: [ + { + storage: localStorage + } + ] } }) \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue index c838aea..91f3c02 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -1,7 +1,4 @@