From 698ec0d61dda16dbe786c728e7b4c55211f0bea3 Mon Sep 17 00:00:00 2001 From: Sanskar Soni Date: Wed, 13 Sep 2023 18:51:15 +0530 Subject: [PATCH 1/3] Implemented: support for single logout --- package-lock.json | 28 ++++++++++++++-------------- package.json | 4 ++-- src/App.vue | 3 ++- src/adapter/index.ts | 4 ++++ src/main.ts | 5 ++++- src/store/modules/user/actions.ts | 10 ++++++++-- src/user-utils/index.ts | 2 +- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 43783617..416aa0de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "@capacitor/core": "^2.4.7", "@hotwax/app-version-info": "^1.0.0", "@hotwax/apps-theme": "^1.1.0", - "@hotwax/dxp-components": "^1.3.4", - "@hotwax/oms-api": "^1.6.0", + "@hotwax/dxp-components": "^1.5.3", + "@hotwax/oms-api": "^1.9.0", "@ionic/core": "6.7.5", "@ionic/vue": "6.7.5", "@ionic/vue-router": "6.7.5", @@ -2180,9 +2180,9 @@ "integrity": "sha512-yokhlpG+eUEao19vaUtCOC5YwcYTb5sf5joGVH17mbb5B1hM4G+P4ZiGiFw2AMmZBKgu94DtHwSyjtdsf4bHug==" }, "node_modules/@hotwax/dxp-components": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@hotwax/dxp-components/-/dxp-components-1.3.4.tgz", - "integrity": "sha512-uw0dmxm19lzsgcLWZ2fQBAZem0d7TkxGR0iSq1c7Bj1mkAJZXH5JbP12w0lYhtes7Q1MjhHPlWljCh6624E9XA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@hotwax/dxp-components/-/dxp-components-1.5.3.tgz", + "integrity": "sha512-6u9CpzkYrgQt1tfMeXRgpXnh1hHYoagagDByX/8nfZ+VziKtSY70bfAves3AipluL3MdJq9WEpOzG0r0a/VA6g==", "dependencies": { "@hotwax/oms-api": "^1.8.1", "luxon": "^3.3.0", @@ -2192,9 +2192,9 @@ } }, "node_modules/@hotwax/oms-api": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@hotwax/oms-api/-/oms-api-1.8.1.tgz", - "integrity": "sha512-pVceiuaOSkRZM3i87+TCEu6uBMDG01JGTG92mCpDUXWmA6nd0BL7QiK1Qr8a9lDTib0M24RVBDCGc/cX6nlxgg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@hotwax/oms-api/-/oms-api-1.9.0.tgz", + "integrity": "sha512-1PcS95vP8PzzlBRwHQRk99eJT0xStBSmNdpdoATRUSlLamxmHZ8RAwAwY3usph5gNy7Z+WzAxl9fJXX9VgWP9g==", "dependencies": { "@types/node-json-transform": "^1.0.0", "axios": "^0.21.1", @@ -34813,9 +34813,9 @@ "integrity": "sha512-yokhlpG+eUEao19vaUtCOC5YwcYTb5sf5joGVH17mbb5B1hM4G+P4ZiGiFw2AMmZBKgu94DtHwSyjtdsf4bHug==" }, "@hotwax/dxp-components": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@hotwax/dxp-components/-/dxp-components-1.3.4.tgz", - "integrity": "sha512-uw0dmxm19lzsgcLWZ2fQBAZem0d7TkxGR0iSq1c7Bj1mkAJZXH5JbP12w0lYhtes7Q1MjhHPlWljCh6624E9XA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@hotwax/dxp-components/-/dxp-components-1.5.3.tgz", + "integrity": "sha512-6u9CpzkYrgQt1tfMeXRgpXnh1hHYoagagDByX/8nfZ+VziKtSY70bfAves3AipluL3MdJq9WEpOzG0r0a/VA6g==", "requires": { "@hotwax/oms-api": "^1.8.1", "luxon": "^3.3.0", @@ -34825,9 +34825,9 @@ } }, "@hotwax/oms-api": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@hotwax/oms-api/-/oms-api-1.8.1.tgz", - "integrity": "sha512-pVceiuaOSkRZM3i87+TCEu6uBMDG01JGTG92mCpDUXWmA6nd0BL7QiK1Qr8a9lDTib0M24RVBDCGc/cX6nlxgg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@hotwax/oms-api/-/oms-api-1.9.0.tgz", + "integrity": "sha512-1PcS95vP8PzzlBRwHQRk99eJT0xStBSmNdpdoATRUSlLamxmHZ8RAwAwY3usph5gNy7Z+WzAxl9fJXX9VgWP9g==", "requires": { "@types/node-json-transform": "^1.0.0", "axios": "^0.21.1", diff --git a/package.json b/package.json index 9baf702c..a2bfcb8b 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "@capacitor/core": "^2.4.7", "@hotwax/app-version-info": "^1.0.0", "@hotwax/apps-theme": "^1.1.0", - "@hotwax/dxp-components": "^1.3.4", - "@hotwax/oms-api": "^1.6.0", + "@hotwax/dxp-components": "^1.5.3", + "@hotwax/oms-api": "^1.9.0", "@ionic/core": "6.7.5", "@ionic/vue": "6.7.5", "@ionic/vue-router": "6.7.5", diff --git a/src/App.vue b/src/App.vue index c9732936..114515fa 100644 --- a/src/App.vue +++ b/src/App.vue @@ -81,7 +81,8 @@ export default defineComponent({ showToast(translate("New version available, please update the app.")); }, async unauthorized() { - this.store.dispatch("user/logout"); + // Mark the user as unauthorised, this will help in not making the logout api call in actions + this.store.dispatch("user/logout", { isUserUnauthorised: true }); const redirectUrl = window.location.origin + '/login'; window.location.href = `${process.env.VUE_APP_LOGIN_URL}?redirectUrl=${redirectUrl}`; } diff --git a/src/adapter/index.ts b/src/adapter/index.ts index 57580697..696d783c 100644 --- a/src/adapter/index.ts +++ b/src/adapter/index.ts @@ -1,8 +1,10 @@ import { api, client, + getConfig, fetchProducts, initialise, + logout, isError, Product, resetConfig, @@ -13,8 +15,10 @@ import { export { api, client, + getConfig, fetchProducts, initialise, + logout, isError, Product, resetConfig, diff --git a/src/main.ts b/src/main.ts index 2dc5d03c..8d2497cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,6 +32,7 @@ import { DateTime } from 'luxon'; import logger from './logger'; import { dxpComponents } from '@hotwax/dxp-components' import { login, logout, loader } from './user-utils'; +import { getConfig, initialise } from '@/adapter' const app = createApp(App) .use(IonicVue, { @@ -48,7 +49,9 @@ const app = createApp(App) login, logout, loader, - appLoginUrl: process.env.VUE_APP_LOGIN_URL as string + appLoginUrl: process.env.VUE_APP_LOGIN_URL as string, + getConfig, + initialise }); // Filters are removed in Vue 3 and global filter introduced https://v3.vuejs.org/guide/migration/filters.html#global-filters diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 5303eac4..43c5b042 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -5,7 +5,7 @@ import UserState from './UserState' import * as types from './mutation-types' import { hasError, showToast } from '@/utils' import { translate } from '@/i18n' -import { updateInstanceUrl, updateToken, resetConfig } from '@/adapter' +import { logout, updateInstanceUrl, updateToken, resetConfig } from '@/adapter' import logger from "@/logger"; import { useAuthStore } from '@hotwax/dxp-components'; @@ -61,7 +61,13 @@ const actions: ActionTree = { /** * Logout user */ - async logout ({ commit }) { + async logout ({ commit }, payload) { + // Calling the logout api to flag the user as logged out, only when user is authorised + // if the user is already unauthorised then not calling the logout api as it returns 401 again that results in a loop, thus there is no need to call logout api if the user is unauthorised + if (!payload?.isUserUnauthorised) { + await logout(); + } + const authStore = useAuthStore() // TODO add any other tasks if need diff --git a/src/user-utils/index.ts b/src/user-utils/index.ts index faf98e48..44c37aba 100644 --- a/src/user-utils/index.ts +++ b/src/user-utils/index.ts @@ -4,7 +4,7 @@ import { loadingController } from '@ionic/vue' const login = async (payload: any) => store.dispatch('user/login', payload); -const logout = async () => store.dispatch('user/logout'); +const logout = async (payload: any) => store.dispatch('user/logout', payload); const loader = { value: null as any, From 147e1f08b1996253ff1836948e651f29ab14d73c Mon Sep 17 00:00:00 2001 From: Sanskar Soni Date: Mon, 25 Sep 2023 18:52:45 +0530 Subject: [PATCH 2/3] Improved: code to redirect the user to SSO screen when enabled otherwise redirect to launchpad on logout, and added support to loader for custom message in case of logout --- .env.example | 1 - src/App.vue | 9 ++++++--- src/locales/en.json | 2 +- src/store/modules/user/actions.ts | 23 ++++++++++++++++++++++- src/views/Settings.vue | 9 ++++++--- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index c708e5a2..f4ec277f 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,6 @@ VUE_APP_VIEW_SIZE=10 VUE_APP_DATE_FORMAT=MM/dd/yyyy VUE_APP_PERMISSION_ID= VUE_APP_ALIAS={} -VUE_APP_DEFAULT_LOG_LEVEL="error" VUE_APP_MAPPING_TYPES={"PO": "PO_MAPPING_PREF","RSTINV": "INV_MAPPING_PREF"} VUE_APP_MAPPING_PO={"orderId": { "label": "Order ID", "required": true }, "productSku": { "label": "Shopify product SKU", "required": true },"orderDate": { "label": "Arrival date", "required": true }, "quantity": { "label": "Ordered quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }} VUE_APP_MAPPING_RSTINV={"productSku": { "label": "Product SKU", "required": true }, "quantity": { "label": "Quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }, "locationSeqId": { "label": "Facility Location", "required": true }} diff --git a/src/App.vue b/src/App.vue index 114515fa..8c0da02f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -34,13 +34,16 @@ export default defineComponent({ } }, methods: { - async presentLoader() { + async presentLoader(options = { message: '', backdropDismiss: true }) { + // When having a custom message remove already existing loader + if(options.message && this.loader) this.dismissLoader(); + if (!this.loader) { this.loader = await loadingController .create({ - message: this.$t("Click the backdrop to dismiss."), + message: options.message ? this.$t(options.message) : this.$t("Click the backdrop to dismiss."), translucent: true, - backdropDismiss: true + backdropDismiss: options.backdropDismiss }); } this.loader.present(); diff --git a/src/locales/en.json b/src/locales/en.json index a7200c71..a0cc2caa 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -59,7 +59,6 @@ "items": "items", "items selected": "items selected", "Import": "Import", - "Inventory": "Inventory", "Instance Url": "Instance Url", "Items": "Items", "Invalid input": "Invalid input", @@ -71,6 +70,7 @@ "Loading": "Loading", "Login": "Login", "Logging in": "Logging in", + "Logging out": "Logging out", "Logout": "Logout", "Mapping": "Mapping", "Mapping details": "Mapping details", diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 43c5b042..5004d996 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -8,6 +8,7 @@ import { translate } from '@/i18n' import { logout, updateInstanceUrl, updateToken, resetConfig } from '@/adapter' import logger from "@/logger"; import { useAuthStore } from '@hotwax/dxp-components'; +import emitter from '@/event-bus' const actions: ActionTree = { @@ -62,10 +63,22 @@ const actions: ActionTree = { * Logout user */ async logout ({ commit }, payload) { + // store the url on which we need to redirect the user after logout api completes in case of SSO enabled + let redirectionUrl = '' + + emitter.emit('presentLoader', { message: 'Logging out', backdropDismiss: false }) + // Calling the logout api to flag the user as logged out, only when user is authorised // if the user is already unauthorised then not calling the logout api as it returns 401 again that results in a loop, thus there is no need to call logout api if the user is unauthorised if (!payload?.isUserUnauthorised) { - await logout(); + let resp = await logout(); + + // Added logic to remove the `//` from the resp as in case of get request we are having the extra characters and in case of post we are having 403 + resp = JSON.parse(resp.startsWith('//') ? resp.replace('//', '') : resp) + + if(resp.logoutAuthType == 'SAML2SSO') { + redirectionUrl = resp.logoutUrl + } } const authStore = useAuthStore() @@ -81,6 +94,14 @@ const actions: ActionTree = { // reset plugin state on logout authStore.$reset() + + // If we get any url in logout api resp then we will redirect the user to the url + if(redirectionUrl) { + window.location.href = redirectionUrl + } + + emitter.emit('dismissLoader') + return redirectionUrl; }, /** diff --git a/src/views/Settings.vue b/src/views/Settings.vue index 66848c95..42d6bcfe 100644 --- a/src/views/Settings.vue +++ b/src/views/Settings.vue @@ -196,9 +196,12 @@ export default defineComponent({ this.sampleDateTime = DateTime.now().toFormat(this.dateTimeFormat); }, logout () { - this.store.dispatch('user/logout').then(() => { - const redirectUrl = window.location.origin + '/login' - window.location.href = `${process.env.VUE_APP_LOGIN_URL}?isLoggedOut=true&redirectUrl=${redirectUrl}` + this.store.dispatch('user/logout', { isUserUnauthorised: false }).then((redirectionUrl) => { + // if not having redirection url then redirect the user to launchpad + if(!redirectionUrl) { + const redirectUrl = window.location.origin + '/login' + window.location.href = `${process.env.VUE_APP_LOGIN_URL}?isLoggedOut=true&redirectUrl=${redirectUrl}` + } }) }, goToLaunchpad() { From 0f90d0d501fd54b762bacbe1921d0c19f2375fbb Mon Sep 17 00:00:00 2001 From: Sanskar Soni Date: Mon, 9 Oct 2023 18:27:40 +0530 Subject: [PATCH 3/3] Implemented: parsing logic in try catch as in some case the logout api makes redirection, and then we are unable to parse the resp --- src/store/modules/user/actions.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 5004d996..4a2cc6b6 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -70,13 +70,20 @@ const actions: ActionTree = { // Calling the logout api to flag the user as logged out, only when user is authorised // if the user is already unauthorised then not calling the logout api as it returns 401 again that results in a loop, thus there is no need to call logout api if the user is unauthorised - if (!payload?.isUserUnauthorised) { - let resp = await logout(); + if(!payload?.isUserUnauthorised) { + let resp; - // Added logic to remove the `//` from the resp as in case of get request we are having the extra characters and in case of post we are having 403 - resp = JSON.parse(resp.startsWith('//') ? resp.replace('//', '') : resp) + // wrapping the parsing logic in try catch as in some case the logout api makes redirection, and then we are unable to parse the resp and thus the logout process halts + try { + resp = await logout(); - if(resp.logoutAuthType == 'SAML2SSO') { + // Added logic to remove the `//` from the resp as in case of get request we are having the extra characters and in case of post we are having 403 + resp = JSON.parse(resp.startsWith('//') ? resp.replace('//', '') : resp) + } catch(err) { + logger.error('Error parsing data', err) + } + + if(resp?.logoutAuthType == 'SAML2SSO') { redirectionUrl = resp.logoutUrl } }