From 1263afb21e2759630a0a49159b64f1cb9f4a900a Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Mon, 10 Apr 2023 21:36:37 +0200
Subject: [PATCH] Fixed some problems with async calls

---
 fda-authentication-service/dbrepo-realm.json |  54 ++---
 fda-ui/api/authentication.service.js         |  45 ++--
 fda-ui/api/container.service.js              |  69 +++---
 fda-ui/layouts/default.vue                   | 227 +++++++++----------
 fda-ui/pages/login.vue                       |  27 ++-
 fda-ui/plugins/axios.js                      |   1 +
 fda-ui/server-middleware/store.js            |  48 +++-
 7 files changed, 258 insertions(+), 213 deletions(-)

diff --git a/fda-authentication-service/dbrepo-realm.json b/fda-authentication-service/dbrepo-realm.json
index c5005faeb2..c9ef98fe87 100644
--- a/fda-authentication-service/dbrepo-realm.json
+++ b/fda-authentication-service/dbrepo-realm.json
@@ -870,7 +870,7 @@
   "otpPolicyLookAheadWindow" : 1,
   "otpPolicyPeriod" : 30,
   "otpPolicyCodeReusable" : false,
-  "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ],
+  "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName", "totpAppFreeOTPName" ],
   "webAuthnPolicyRpEntityName" : "keycloak",
   "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
   "webAuthnPolicyRpId" : "",
@@ -1087,8 +1087,8 @@
         "jsonType.label" : "String"
       }
     } ],
-    "defaultClientScopes" : [ ],
-    "optionalClientScopes" : [ "address", "profile", "roles", "web-origins", "rabbitmq.read:*/*", "acr", "rabbitmq.write:*/*", "phone", "offline_access", "attributes", "microprofile-jwt", "email", "rabbitmq.configure:*/*" ]
+    "defaultClientScopes" : [ "profile", "roles", "attributes" ],
+    "optionalClientScopes" : [ "rabbitmq.read:*/*", "web-origins", "acr", "rabbitmq.write:*/*", "address", "phone", "offline_access", "microprofile-jwt", "email", "rabbitmq.configure:*/*" ]
   }, {
     "id" : "25741f6b-4867-4138-8238-6345c6ba8702",
     "clientId" : "rabbitmq-client",
@@ -1815,7 +1815,7 @@
       "subType" : "anonymous",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "saml-role-list-mapper" ]
+        "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper" ]
       }
     }, {
       "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
@@ -1841,7 +1841,7 @@
       "subType" : "authenticated",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper" ]
+        "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ]
       }
     } ],
     "org.keycloak.userprofile.UserProfileProvider" : [ {
@@ -1899,7 +1899,7 @@
   "internationalizationEnabled" : false,
   "supportedLocales" : [ ],
   "authenticationFlows" : [ {
-    "id" : "6230a38e-9a93-4ef4-a522-ebd0a0b5ba7a",
+    "id" : "ffedba28-fef3-4e64-9b37-b3270858aa2a",
     "alias" : "Account verification options",
     "description" : "Method with which to verity the existing account",
     "providerId" : "basic-flow",
@@ -1921,7 +1921,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "f30c0cd5-9380-4297-87a9-2a3feee031ed",
+    "id" : "34232f15-241f-4827-866b-2b03720a6885",
     "alias" : "Authentication Options",
     "description" : "Authentication options.",
     "providerId" : "basic-flow",
@@ -1950,7 +1950,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "f7de5b0c-ebac-403b-acf1-3bb21ffeec22",
+    "id" : "871385ad-06e0-4bf8-b366-ffc256389f1c",
     "alias" : "Browser - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -1972,7 +1972,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "8924d975-c78f-4798-9d81-2a40d7e5188a",
+    "id" : "a9fbd9c0-b524-40fb-bc90-23317dc3d611",
     "alias" : "Direct Grant - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -1994,7 +1994,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "288e798c-84a3-4368-bb61-bb29c39d6d2c",
+    "id" : "cf030c87-aeab-453b-a50c-0a4454a5feb9",
     "alias" : "First broker login - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -2016,7 +2016,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "ced94ffb-3056-4582-82ef-06bff9ee166d",
+    "id" : "4c156a62-d234-4af7-a609-3c7a0b58d7cf",
     "alias" : "Handle Existing Account",
     "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
     "providerId" : "basic-flow",
@@ -2038,7 +2038,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "0cb56734-e325-4417-8aef-07237ecddd5a",
+    "id" : "db5115da-e49d-4572-af14-a7cb12cf4424",
     "alias" : "Reset - Conditional OTP",
     "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
     "providerId" : "basic-flow",
@@ -2060,7 +2060,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "d6fdc139-02e9-4799-beed-111a272b22e2",
+    "id" : "37d5aaaa-2514-4973-b5ae-89ac84bc8600",
     "alias" : "User creation or linking",
     "description" : "Flow for the existing/non-existing user alternatives",
     "providerId" : "basic-flow",
@@ -2083,7 +2083,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "b94cb3b9-40ba-45f8-9432-b40e45c94cd3",
+    "id" : "725b445b-02ee-47ba-9ea2-2c3b8007a025",
     "alias" : "Verify Existing Account by Re-authentication",
     "description" : "Reauthentication of existing account",
     "providerId" : "basic-flow",
@@ -2105,7 +2105,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "c2643757-cd92-4b82-b9f1-8d4769495484",
+    "id" : "5af07663-b611-464c-aefc-8da55b76d100",
     "alias" : "browser",
     "description" : "browser based authentication",
     "providerId" : "basic-flow",
@@ -2141,7 +2141,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "07f49e9e-38cd-4d03-90b5-720aa77f5ae6",
+    "id" : "80ebd402-2fa1-42f3-b6b7-885c85ce1849",
     "alias" : "clients",
     "description" : "Base authentication for clients",
     "providerId" : "client-flow",
@@ -2177,7 +2177,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "381cd686-4fc3-47d2-8048-47f07dcb1a52",
+    "id" : "ab147f84-a2bf-4c35-83a4-fc662c9fb4f2",
     "alias" : "direct grant",
     "description" : "OpenID Connect Resource Owner Grant",
     "providerId" : "basic-flow",
@@ -2206,7 +2206,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "02bd79fb-4446-46e5-b881-d58b93ec8557",
+    "id" : "87ecaa80-a8b6-47e5-af44-1915880a4ef7",
     "alias" : "docker auth",
     "description" : "Used by Docker clients to authenticate against the IDP",
     "providerId" : "basic-flow",
@@ -2221,7 +2221,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "f0e72761-34a5-4ddc-9eda-a7b43b45ce4b",
+    "id" : "d79a5c9e-68a8-430e-97b0-197055caa873",
     "alias" : "first broker login",
     "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
     "providerId" : "basic-flow",
@@ -2244,7 +2244,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "752ea7e2-01f2-4cef-967e-e7678d245243",
+    "id" : "4df39289-f633-466c-9dfd-2ca6ff9445d0",
     "alias" : "forms",
     "description" : "Username, password, otp and other auth forms.",
     "providerId" : "basic-flow",
@@ -2266,7 +2266,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "89e9a682-6a84-4688-924e-e5582866d7fd",
+    "id" : "63948a28-ff5b-4a84-8f61-7d1a1e80773e",
     "alias" : "http challenge",
     "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
     "providerId" : "basic-flow",
@@ -2288,7 +2288,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "02a165c7-53d5-4601-a342-de3e23a95054",
+    "id" : "514330ab-3731-4245-86c0-9155d84fb714",
     "alias" : "registration",
     "description" : "registration flow",
     "providerId" : "basic-flow",
@@ -2304,7 +2304,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "6a68e3c0-f1ed-4720-8bbe-458e1077e7eb",
+    "id" : "f66aecac-f51a-4c80-8869-338d89054615",
     "alias" : "registration form",
     "description" : "registration form",
     "providerId" : "form-flow",
@@ -2340,7 +2340,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "7fff0117-d0b4-43ae-82e0-a93abc5dda87",
+    "id" : "7c404e92-1b71-4a64-8d86-763232fc01ea",
     "alias" : "reset credentials",
     "description" : "Reset credentials for a user if they forgot their password or something",
     "providerId" : "basic-flow",
@@ -2376,7 +2376,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "6d2ce2e0-f4d8-4e57-b77f-b681f2a9b99d",
+    "id" : "a69efd34-ba60-4ff2-852a-1704ae3de2a4",
     "alias" : "saml ecp",
     "description" : "SAML ECP Profile Authentication Flow",
     "providerId" : "basic-flow",
@@ -2392,13 +2392,13 @@
     } ]
   } ],
   "authenticatorConfig" : [ {
-    "id" : "9fdb8f42-4d69-495f-a97f-414480c7405f",
+    "id" : "65bc1552-4100-4b2e-b765-4393f09a1af1",
     "alias" : "create unique user config",
     "config" : {
       "require.password.update.after.registration" : "false"
     }
   }, {
-    "id" : "12c376db-bfdb-41fb-bdf9-45e5eda8d879",
+    "id" : "7f26c4ed-6eb2-4cab-8ffa-c4da830ffa26",
     "alias" : "review profile config",
     "config" : {
       "update.profile.on.first.login" : "missing"
diff --git a/fda-ui/api/authentication.service.js b/fda-ui/api/authentication.service.js
index cf36d2cc46..d031ead6ba 100644
--- a/fda-ui/api/authentication.service.js
+++ b/fda-ui/api/authentication.service.js
@@ -40,27 +40,30 @@ class AuthenticationService {
   }
 
   _authenticate (payload) {
-    axios.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(payload), {
-      headers: {
-        'Content-Type': 'application/x-www-form-urlencoded'
-      }
-    }).then((response) => {
-      const authentication = response.data
-      // eslint-disable-next-line camelcase
-      const { access_token, refresh_token } = authentication
-      console.debug('response authenticate', authentication)
-      setToken(access_token)
-      setRefreshToken(refresh_token)
-      return authentication
-    }).catch((error) => {
-      console.error('Failed to authenticate', error)
-      const { code, message, response } = error
-      const { status } = response
-      if (status === 401) {
-        Vue.$toast.error('Invalid username-password combination.')
-      } else {
-        Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`)
-      }
+    return new Promise((resolve, reject) => {
+      axios.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(payload), {
+        headers: {
+          'Content-Type': 'application/x-www-form-urlencoded'
+        }
+      }).then((response) => {
+        const authentication = response.data
+        // eslint-disable-next-line camelcase
+        const { access_token, refresh_token } = authentication
+        console.debug('response authenticate', authentication)
+        setToken(access_token)
+        setRefreshToken(refresh_token)
+        resolve(authentication)
+      }).catch((error) => {
+        console.error('Failed to authenticate', error)
+        const { code, message, response } = error
+        const { status } = response
+        if (status === 401) {
+          Vue.$toast.error('Invalid username-password combination.')
+        } else {
+          Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`)
+        }
+        reject(error)
+      })
     })
   }
 }
diff --git a/fda-ui/api/container.service.js b/fda-ui/api/container.service.js
index 2a6a3297bf..34e069a804 100644
--- a/fda-ui/api/container.service.js
+++ b/fda-ui/api/container.service.js
@@ -20,42 +20,51 @@ class ContainerService {
   }
 
   findOne (id) {
-    api.get(`/api/container/${id}`, { headers: { Accept: 'application/json' } })
-      .then((response) => {
-        const container = response.data
-        console.debug('response container', container)
-        return container
-      }).catch((error) => {
-        const { code, message } = error
-        console.error('Failed to load container', error)
-        Vue.$toast.error(`[${code}] Failed to load container: ${message}`)
-      })
+    return new Promise((resolve, reject) => {
+      api.get(`/api/container/${id}`, { headers: { Accept: 'application/json' } })
+        .then((response) => {
+          const container = response.data
+          console.debug('response container', container)
+          resolve(container)
+        }).catch((error) => {
+          const { code, message } = error
+          console.error('Failed to load container', error)
+          Vue.$toast.error(`[${code}] Failed to load container: ${message}`)
+          reject(error)
+        })
+    })
   }
 
   create (data) {
-    api.post('/api/container', data, { headers: { Accept: 'application/json' } })
-      .then((response) => {
-        const container = response.data
-        console.debug('response container', container)
-        return container
-      }).catch((error) => {
-        const { code, message } = error
-        console.error('Failed to create container', error)
-        Vue.$toast.error(`[${code}] Failed to create container: ${message}`)
-      })
+    return new Promise((resolve, reject) => {
+      api.post('/api/container', data, { headers: { Accept: 'application/json' } })
+        .then((response) => {
+          const container = response.data
+          console.debug('response container', container)
+          resolve(container)
+        }).catch((error) => {
+          const { code, message } = error
+          console.error('Failed to create container', error)
+          Vue.$toast.error(`[${code}] Failed to create container: ${message}`)
+          reject(error)
+        })
+    })
   }
 
   modify (id, action) {
-    api.put(`/api/container/${id}`, { action }, { headers: { Accept: 'application/json' } })
-      .then((response) => {
-        const container = response.data
-        console.debug('response container', container)
-        return container
-      }).catch((error) => {
-        const { code, message } = error
-        console.error('Failed to modify container', error)
-        Vue.$toast.error(`[${code}] Failed to modify container: ${message}`)
-      })
+    return new Promise((resolve, reject) => {
+      api.put(`/api/container/${id}`, { action }, { headers: { Accept: 'application/json' } })
+        .then((response) => {
+          const container = response.data
+          console.debug('response container', container)
+          resolve(container)
+        }).catch((error) => {
+          const { code, message } = error
+          console.error('Failed to modify container', error)
+          Vue.$toast.error(`[${code}] Failed to modify container: ${message}`)
+          reject(error)
+        })
+    })
   }
 }
 
diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue
index 0a00d09396..d342b1e299 100644
--- a/fda-ui/layouts/default.vue
+++ b/fda-ui/layouts/default.vue
@@ -130,7 +130,6 @@
 
 <script>
 import { isDeveloper } from '@/utils'
-import { findUser, getThemeDark, tokenToRoles } from '@/api/user'
 export default {
   name: 'DefaultLayout',
   data () {
@@ -230,14 +229,14 @@ export default {
     }
   },
   mounted () {
-    this.loadUser()
-    this.setTheme()
-    this.loadDatabase()
-      .then(() => {
-        this.loadIdentifier()
-        this.loadTable()
-      })
-    this.loadAccess()
+    // this.loadUser()
+    // this.setTheme()
+    // this.loadDatabase()
+    //   .then(() => {
+    // this.loadIdentifier()
+    // this.loadTable()
+    // })
+    // this.loadAccess()
     if (this.$route.query && this.$route.query.q) {
       this.search = this.$route.query.q
     }
@@ -261,111 +260,111 @@ export default {
       this.$vuetify.theme.dark = false
       this.$router.push('/container')
     },
-    async loadUser () {
-      if (!this.token) {
-        return
-      }
-      try {
-        this.loadingUser = true
-        const res = await findUser(this.token)
-        const user = res.data
-        console.debug('user', user)
-        this.$store.commit('SET_USER', user)
-        const roles = tokenToRoles(this.token)
-        this.$store.commit('SET_ROLES', roles)
-        this.$vuetify.theme.dark = getThemeDark(user)
-        this.loading = false
-      } catch (error) {
-        console.error('Failed to load user', error)
-        const { status } = error.response
-        if (status === 401) {
-          console.error('Token expired', error)
-          this.$toast.warning('Login has expired')
-          this.logout()
-        } else {
-          console.error('user data', error)
-          this.$toast.error('Failed to load user')
-          this.error = true
-        }
-      }
-      this.loadingUser = false
-    },
-    async loadDatabase () {
-      if (!this.$route.params.container_id || !this.$route.params.database_id) {
-        return
-      }
-      try {
-        this.loading = true
-        const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}`, this.config)
-        this.$store.commit('SET_DATABASE', res.data)
-        console.debug('database', this.database)
-      } catch (err) {
-        console.error('Could not load database', err)
-        this.$toast.error('Could not load database')
-      }
-      this.loading = false
-    },
-    async loadTable () {
-      if (!this.$route.params.container_id || !this.$route.params.database_id || !this.$route.params.table_id) {
-        return
-      }
-      try {
-        this.loading = true
-        const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`, this.config)
-        this.$store.commit('SET_TABLE', res.data)
-        console.debug('table', this.table)
-      } catch (error) {
-        const { status } = error.response
-        if (status === 405) {
-          const table = this.database.tables.filter(t => t.id === Number(this.$route.params.table_id))[0]
-          this.$store.commit('SET_TABLE', table)
-        } else {
-          const { message } = error.response.data
-          console.error('Failed to load table', error)
-          this.$toast.error(`Failed to load table: ${message}`)
-        }
-      }
-      this.loading = false
-    },
-    async loadAccess () {
-      if (!this.$route.params.container_id || !this.$route.params.database_id) {
-        return
-      }
-      if (!this.token) {
-        return
-      }
-      try {
-        this.loading = true
-        const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/access`, this.config)
-        this.access = res.data
-        this.$store.commit('SET_ACCESS', res.data)
-        console.debug('access', this.access)
-      } catch (err) {
-        this.$store.commit('SET_ACCESS', null)
-        const { status } = err.response
-        if (status !== 401 && status !== 403) {
-          console.error('Failed to check access', err)
-          this.$toast.error('Failed to check access')
-        }
-      }
-      this.loading = false
-    },
-    async loadIdentifier () {
-      if (!this.database || 'identifier' in this.database) {
-        return
-      }
-      try {
-        this.loading = true
-        const res = await this.$axios.get(`/api/pid/${this.database.identifier.id}`, this.config)
-        const db = this.database
-        db.identifier = res.data
-        this.$store.commit('SET_DATABASE', db)
-      } catch (err) {
-        console.error('Failed to load identifier', err)
-        this.$toast.error('Failed to load identifier')
-      }
-      this.loading = false
-    },
+    // async loadUser () {
+    //   if (!this.token) {
+    //     return
+    //   }
+    //   try {
+    //     this.loadingUser = true
+    //     const res = await findUser(this.token)
+    //     const user = res.data
+    //     console.debug('user', user)
+    //     this.$store.commit('SET_USER', user)
+    //     const roles = tokenToRoles(this.token)
+    //     this.$store.commit('SET_ROLES', roles)
+    //     this.$vuetify.theme.dark = getThemeDark(user)
+    //     this.loading = false
+    //   } catch (error) {
+    //     console.error('Failed to load user', error)
+    //     const { status } = error.response
+    //     if (status === 401) {
+    //       console.error('Token expired', error)
+    //       this.$toast.warning('Login has expired')
+    //       this.logout()
+    //     } else {
+    //       console.error('user data', error)
+    //       this.$toast.error('Failed to load user')
+    //       this.error = true
+    //     }
+    //   }
+    //   this.loadingUser = false
+    // },
+    // async loadDatabase () {
+    //   if (!this.$route.params.container_id || !this.$route.params.database_id) {
+    //     return
+    //   }
+    //   try {
+    //     this.loading = true
+    //     const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}`, this.config)
+    //     this.$store.commit('SET_DATABASE', res.data)
+    //     console.debug('database', this.database)
+    //   } catch (err) {
+    //     console.error('Could not load database', err)
+    //     this.$toast.error('Could not load database')
+    //   }
+    //   this.loading = false
+    // },
+    // async loadTable () {
+    //   if (!this.$route.params.container_id || !this.$route.params.database_id || !this.$route.params.table_id) {
+    //     return
+    //   }
+    //   try {
+    //     this.loading = true
+    //     const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`, this.config)
+    //     this.$store.commit('SET_TABLE', res.data)
+    //     console.debug('table', this.table)
+    //   } catch (error) {
+    //     const { status } = error.response
+    //     if (status === 405) {
+    //       const table = this.database.tables.filter(t => t.id === Number(this.$route.params.table_id))[0]
+    //       this.$store.commit('SET_TABLE', table)
+    //     } else {
+    //       const { message } = error.response.data
+    //       console.error('Failed to load table', error)
+    //       this.$toast.error(`Failed to load table: ${message}`)
+    //     }
+    //   }
+    //   this.loading = false
+    // },
+    // async loadAccess () {
+    //   if (!this.$route.params.container_id || !this.$route.params.database_id) {
+    //     return
+    //   }
+    //   if (!this.token) {
+    //     return
+    //   }
+    //   try {
+    //     this.loading = true
+    //     const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/access`, this.config)
+    //     this.access = res.data
+    //     this.$store.commit('SET_ACCESS', res.data)
+    //     console.debug('access', this.access)
+    //   } catch (err) {
+    //     this.$store.commit('SET_ACCESS', null)
+    //     const { status } = err.response
+    //     if (status !== 401 && status !== 403) {
+    //       console.error('Failed to check access', err)
+    //       this.$toast.error('Failed to check access')
+    //     }
+    //   }
+    //   this.loading = false
+    // },
+    // async loadIdentifier () {
+    //   if (!this.database || 'identifier' in this.database) {
+    //     return
+    //   }
+    //   try {
+    //     this.loading = true
+    //     const res = await this.$axios.get(`/api/pid/${this.database.identifier.id}`, this.config)
+    //     const db = this.database
+    //     db.identifier = res.data
+    //     this.$store.commit('SET_DATABASE', db)
+    //   } catch (err) {
+    //     console.error('Failed to load identifier', err)
+    //     this.$toast.error('Failed to load identifier')
+    //   }
+    //   this.loading = false
+    // },
     retrieve () {
       this.$router.push({ path: '/search', query: { q: this.search } })
     },
diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue
index 68e9b08d82..2df061095a 100644
--- a/fda-ui/pages/login.vue
+++ b/fda-ui/pages/login.vue
@@ -58,7 +58,6 @@
 </template>
 
 <script>
-import { getThemeDark, findUser } from '@/api/user'
 import AuthenticationService from '@/api/authentication.service'
 export default {
   data () {
@@ -108,23 +107,23 @@ export default {
     submit () {
       this.$refs.form.validate()
     },
-    async login () {
+    login () {
       this.loading = true
       AuthenticationService.authenticatePlain(this.username, this.password)
-      await this.$router.push({ path: this.$route.query.redirect ? this.$route.query.redirect : '/container' })
+        .then(() => this.$router.push({ path: '/container' }))
       this.loading = false
     },
-    async setTheme () {
-      try {
-        const res = await findUser(this.token)
-        const user = res.data
-        console.debug('user', user)
-        this.$store.commit('SET_USER', user)
-        this.$vuetify.theme.dark = getThemeDark(user)
-      } catch (error) {
-        console.error('Failed to set theme', error)
-      }
-    },
+    // async setTheme () {
+    //   try {
+    //     const res = await findUser(this.token)
+    //     const user = res.data
+    //     console.debug('user', user)
+    //     this.$store.commit('SET_USER', user)
+    //     this.$vuetify.theme.dark = getThemeDark(user)
+    //   } catch (error) {
+    //     console.error('Failed to set theme', error)
+    //   }
+    // },
     signup () {
       this.$router.push('/signup')
     },
diff --git a/fda-ui/plugins/axios.js b/fda-ui/plugins/axios.js
index 909161f081..46eb628416 100644
--- a/fda-ui/plugins/axios.js
+++ b/fda-ui/plugins/axios.js
@@ -18,6 +18,7 @@ api.interceptors.request.use((config) => {
       /* refresh token expired */
       setToken(null)
       setRefreshToken(null)
+      console.warn('Refresh token expired')
     }
     AuthenticationService.authenticateToken(refreshToken)
     return config
diff --git a/fda-ui/server-middleware/store.js b/fda-ui/server-middleware/store.js
index 8a2a1e0dac..4c63653242 100644
--- a/fda-ui/server-middleware/store.js
+++ b/fda-ui/server-middleware/store.js
@@ -1,23 +1,57 @@
 export function setToken (value) {
-  localStorage.setItem('token', JSON.stringify(value))
+  const state = _getState()
+  state.token = value
+  _setState(state)
 }
 
 export function getToken () {
-  return JSON.parse(localStorage.getItem('token'))
+  const state = _getState()
+  return state.token
 }
 
 export function setRefreshToken (value) {
-  localStorage.setItem('refresh_token', JSON.stringify(value))
+  const state = _getState()
+  state.refresh_token = value
+  _setState(state)
 }
 
 export function getRefreshToken () {
-  return JSON.parse(localStorage.getItem('refresh_token'))
+  const state = _getState()
+  return state.refresh_token
 }
 
-export function setUser (user) {
-  localStorage.setItem('user', JSON.stringify(user))
+export function setUser (value) {
+  const state = _getState()
+  state.user = value
+  _setState(state)
 }
 
 export function getUser () {
-  return JSON.parse(localStorage.getItem('user'))
+  const state = _getState()
+  return state.user
+}
+
+export function _getState () {
+  if (!JSON.parse(localStorage.getItem('vuex'))) {
+    init()
+  }
+  return JSON.parse(localStorage.getItem('vuex'))
+}
+
+function _setState (state) {
+  const json = JSON.stringify(state)
+  localStorage.setItem('vuex', json)
+}
+
+function init () {
+  const state = {
+    token: null,
+    roles: [],
+    user: null,
+    database: null,
+    table: null,
+    access: null
+  }
+  localStorage.setItem('vuex', JSON.stringify(state))
+  console.debug('initialized vuex state')
 }
-- 
GitLab