Some work on login process

* Forward already autheticated users
* If not auteticated forward back to login
* Clear current user if no valid session
This commit is contained in:
Ferdinand Thiessen 2020-11-09 03:40:51 +01:00
parent 8c1dffc003
commit b479e3ad48
8 changed files with 97 additions and 55 deletions

19
package-lock.json generated
View File

@ -1719,6 +1719,12 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
"dev": true "dev": true
},
"vue-router": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz",
"integrity": "sha512-khkrcUIzMcI1rDcNtqkvLwfRFzB97GmJEsPAQdj7t/VvpGhmXLOkUfhc+Ah8CvpSXGXwuWuQO+x8Sy/xDhXZIA==",
"dev": true
} }
} }
}, },
@ -4262,9 +4268,9 @@
} }
}, },
"core-js": { "core-js": {
"version": "3.6.5", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA=="
}, },
"core-js-compat": { "core-js-compat": {
"version": "3.6.5", "version": "3.6.5",
@ -13650,10 +13656,9 @@
} }
}, },
"vue-router": { "vue-router": {
"version": "3.2.0", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.3.2.tgz",
"integrity": "sha512-khkrcUIzMcI1rDcNtqkvLwfRFzB97GmJEsPAQdj7t/VvpGhmXLOkUfhc+Ah8CvpSXGXwuWuQO+x8Sy/xDhXZIA==", "integrity": "sha512-5sEbcfb7MW8mY8lbUVbF4kgcipGXsagkM/X+pb6n0MhjP+RorWIUTPAPSqgPaiPOxVCXgAItBl8Vwz8vq78faA=="
"dev": true
}, },
"vue-server-renderer": { "vue-server-renderer": {
"version": "2.6.12", "version": "2.6.12",

View File

@ -13,8 +13,9 @@
"@quasar/extras": "^1.9.10", "@quasar/extras": "^1.9.10",
"@vue/composition-api": "^0.6.4", "@vue/composition-api": "^0.6.4",
"axios": "^0.18.1", "axios": "^0.18.1",
"core-js": "^3.6.5", "core-js": "^3.7.0",
"quasar": "^1.14.3" "quasar": "^1.14.3",
"vue-router": "3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app": "^2.1.6", "@quasar/app": "^2.1.6",
@ -34,15 +35,14 @@
"last 10 Chrome versions", "last 10 Chrome versions",
"last 10 Firefox versions", "last 10 Firefox versions",
"last 4 Edge versions", "last 4 Edge versions",
"last 7 Safari versions", "last 6 Safari versions",
"last 8 Android versions", "last 8 Android versions",
"last 8 ChromeAndroid versions", "last 8 ChromeAndroid versions",
"last 8 FirefoxAndroid versions", "last 8 FirefoxAndroid versions",
"last 10 iOS versions", "last 10 iOS versions"
"last 5 Opera versions"
], ],
"engines": { "engines": {
"node": ">= 10.18.1", "node": ">= 11.0.0",
"npm": ">= 6.13.4", "npm": ">= 6.13.4",
"yarn": ">= 1.21.1" "yarn": ">= 1.21.1"
} }

View File

@ -36,10 +36,13 @@ export default boot<Store<StateInterface>>(({ Vue, store }) => {
error => { error => {
if (error) { if (error) {
const e = <AxiosError>error; const e = <AxiosError>error;
if (e.code === 'ECONNABORTED' || e.response && e.response.status === 502) { if (
e.code === 'ECONNABORTED' ||
(e.response && e.response.status === 502)
) {
store.commit('session/setOffline', true); store.commit('session/setOffline', true);
} else if (e.response && e.response.status == 401) { } else if (e.response && e.response.status == 401) {
return store.dispatch('session/clearup'); return store.dispatch('session/clearCurrent');
} }
} }
return Promise.reject(error); return Promise.reject(error);

View File

@ -35,7 +35,13 @@ export default boot<Store<StateInterface>>(({ router, store }) => {
next({ name: 'login', query: { redirect: to.fullPath } }); next({ name: 'login', query: { redirect: to.fullPath } });
} }
} else { } else {
next(); if (store.state.user.currentUser && !to.params['logout']) {
// Called login while already logged in
void next({ name: 'user-main' });
} else {
// Not logged in or from logout
next();
}
} }
}); });
}); });

View File

@ -105,7 +105,7 @@
import EssentialLink from 'components/navigation/EssentialLink.vue'; import EssentialLink from 'components/navigation/EssentialLink.vue';
import ShortCutLink from 'components/navigation/ShortCutLink.vue'; import ShortCutLink from 'components/navigation/ShortCutLink.vue';
import BackendOffline from 'components/loading/BackendOffline.vue'; import BackendOffline from 'components/loading/BackendOffline.vue';
import { Screen } from 'quasar'; import { Screen, Loading } from 'quasar';
import { defineComponent, ref, computed } from '@vue/composition-api'; import { defineComponent, ref, computed } from '@vue/composition-api';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
@ -180,11 +180,11 @@ export default defineComponent({
}); });
function logout() { function logout() {
const store = <Store<StateInterface>>ctx.root.$store; Loading.show({ message: 'Session wird abgemeldet' });
store (<Store<StateInterface>>ctx.root.$store)
.dispatch('session/logout', store.state.session.currentSession?.token) .dispatch('session/logout')
.catch(error => { .finally(() => {
console.warn(error); Loading.hide();
}); });
} }

View File

@ -35,10 +35,14 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from '@vue/composition-api'; import { defineComponent, ref } from '@vue/composition-api';
import { Loading } from 'quasar';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
setup(_, { root }) { setup(_, { root }) {
const mainRoute = { name: 'user-main' };
/* Stuff for the real login page */
const userid = ref(''); const userid = ref('');
const password = ref(''); const password = ref('');
const rules = [ const rules = [
@ -46,13 +50,22 @@ export default defineComponent({
]; ];
function doLogin() { function doLogin() {
Loading.show({
message: 'Du wirst angemeldet'
});
root.$store root.$store
.dispatch('session/login', { .dispatch('session/login', {
userid: userid.value, userid: userid.value,
password: password.value password: password.value
}) })
.catch(error => { .then(() => {
console.warn(error); const x = root.$route.query['redirect'];
void root.$router.replace(
typeof x === 'string' ? { path: x } : mainRoute
);
})
.finally(() => {
Loading.hide();
}); });
} }

View File

@ -4,7 +4,7 @@ import { StateInterface } from 'src/store';
import { axios } from 'src/boot/axios'; import { axios } from 'src/boot/axios';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { Router } from 'src/router'; import { Router } from 'src/router';
import { LocalStorage, Loading } from 'quasar'; import { LocalStorage } from 'quasar';
export interface SessionInterface { export interface SessionInterface {
currentSession?: FG.Session; currentSession?: FG.Session;
@ -13,7 +13,11 @@ export interface SessionInterface {
backendOffline: boolean; backendOffline: boolean;
} }
function loadFromLocal() { /**
* Load current session from LocalStorage
* Used when we were already authenticated using this browser
*/
function loadCurrentSession() {
const session = LocalStorage.getItem<FG.Session>('currentSession'); const session = LocalStorage.getItem<FG.Session>('currentSession');
if (session) session.expires = new Date(session.expires); if (session) session.expires = new Date(session.expires);
return session; return session;
@ -21,7 +25,7 @@ function loadFromLocal() {
const state: SessionInterface = { const state: SessionInterface = {
sessions: [], sessions: [],
currentSession: loadFromLocal() || undefined, currentSession: loadCurrentSession() || undefined,
loading: false, loading: false,
backendOffline: false backendOffline: false
}; };
@ -47,11 +51,13 @@ const mutations: MutationTree<SessionInterface> = {
}; };
const actions: ActionTree<SessionInterface, StateInterface> = { const actions: ActionTree<SessionInterface, StateInterface> = {
/** Used to authenticate the user
* Setting current Session, User and Permissions.
* @param param0 Context
* @param data Credentitals
*/
login({ commit }, data: LoginData) { login({ commit }, data: LoginData) {
Loading.show({ return axios
message: 'Du wirst angemeldet'
});
void axios
.post('/auth', data) .post('/auth', data)
.then((response: AxiosResponse<LoginResponse>) => { .then((response: AxiosResponse<LoginResponse>) => {
response.data.session.expires = new Date(response.data.session.expires); response.data.session.expires = new Date(response.data.session.expires);
@ -60,38 +66,38 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
commit('user/setCurrentPermissions', response.data.permissions, { commit('user/setCurrentPermissions', response.data.permissions, {
root: true root: true
}); });
})
.catch(error => console.warn(error))
.finally(() => {
void Router.push({ name: 'user-main' });
Loading.hide();
}); });
}, },
/** /**
* Logout from current session * Logout from current session
* Alias of deleteSession with current session as target
*/ */
logout({ dispatch, rootState }) { logout({ dispatch, rootState }) {
Loading.show({ message: 'Session wird abgemeldet' }); if (rootState.session.currentSession) {
dispatch('deleteSession', rootState.session.currentSession?.token).finally( dispatch('deleteSession', rootState.session.currentSession.token).catch(
() => { error => {
Loading.hide(); console.log(error);
} void dispatch('clearCurrent', false);
); }
);
} else {
void dispatch('clearCurrent', false);
}
}, },
/** /**
* Delete a given session * Delete a given session
*/ */
deleteSession({ commit, dispatch, rootState }, token: string | null) { deleteSession({ commit, dispatch, rootState }, token: string) {
if (token === null) return;
commit('setLoading', true); commit('setLoading', true);
axios axios
.delete(`/auth/${token}`) .delete(`/auth/${token}`)
.then(() => { .then(() => {
if (token === rootState.session.currentSession?.token) { if (token === rootState.session.currentSession?.token) {
void dispatch('clearup'); void dispatch('clearCurrent', false);
} else { } else {
commit('getSessions'); dispatch('getSessions').catch(error => {
throw error;
});
} }
}) })
.catch((error: AxiosError) => { .catch((error: AxiosError) => {
@ -101,15 +107,23 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
commit('setLoading', false); commit('setLoading', false);
}); });
}, },
clearup({ commit }) { /**
commit('clearCurrentSession'); * Clear current session and logged in user
commit('user/clearCurrentUser', null, { root: true }); */
void Router.push({ name: 'login' }); clearCurrent({ commit }, redirect = true) {
void Router.push({
name: 'login',
query: redirect ? { redirect: Router.currentRoute.fullPath } : {},
params: { logout: 'true' }
}).then(() => {
commit('clearCurrentSession');
commit('user/clearCurrentUser', null, { root: true });
});
}, },
/** /**
* Get all sessions from current User * Get all sessions from current User
*/ */
getSessions({ commit, state, dispatch }) { getSessions({ commit, state }) {
commit('setLoading', true); commit('setLoading', true);
axios axios
.get('/auth') .get('/auth')
@ -122,11 +136,11 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
return session.token === state.currentSession?.token; return session.token === state.currentSession?.token;
}); });
if (currentSession) { if (currentSession) {
void dispatch('setCurrentSession', currentSession); commit('setCurrentSession', currentSession);
} }
}) })
.catch(error => { .catch(error => {
console.exception(error); throw error;
}) })
.finally(() => { .finally(() => {
commit('setLoading', false); commit('setLoading', false);

View File

@ -2,6 +2,7 @@
"extends": "@quasar/app/tsconfig-preset", "extends": "@quasar/app/tsconfig-preset",
"compilerOptions": { "compilerOptions": {
"types": ["node", "webpack-env", "@quasar/app"], "types": ["node", "webpack-env", "@quasar/app"],
"baseUrl": "." "baseUrl": ".",
"lib": ["DOM", "ES2019"]
} }
} }