diff --git a/dbrepo-ui/bun.lockb b/dbrepo-ui/bun.lockb
index 34e1f3dd06864bd64d099c2212a7b45cb5140d8f..35d4d918b6b74562ce445a20d60ebc12b72ae319 100755
Binary files a/dbrepo-ui/bun.lockb and b/dbrepo-ui/bun.lockb differ
diff --git a/dbrepo-ui/components/user/UserToolbar.vue b/dbrepo-ui/components/user/UserToolbar.vue
index b7452e52c77ca5b10fd4f7f8f070821551a71f60..3a16503673ac0599f827fb14b4e4c9517169c85d 100644
--- a/dbrepo-ui/components/user/UserToolbar.vue
+++ b/dbrepo-ui/components/user/UserToolbar.vue
@@ -11,32 +11,35 @@
           <v-tab
             :text="$t('toolbars.user.authentication')"
             to="/user/authentication" />
-          <v-tab
-            :text="$t('toolbars.user.developer')"
-            to="/user/developer" />
         </v-tabs>
       </template>
     </v-toolbar>
   </div>
 </template>
 
+<script setup>
+definePageMeta({
+  middleware: ['auth']
+})
+const { $oidc } = useNuxtApp()
+</script>
 <script>
-import { useUserStore } from '@/stores/user'
-
 export default {
   data () {
     return {
-      tab: null,
-      userStore: useUserStore()
+      tab: null
     }
   },
   computed: {
     user () {
-      return this.userStore.getUser
+      return this.$oidc.user
     },
     roles () {
-      return this.userStore.getRoles
-    }
+      if (!this.user || !this.user.realm_access) {
+        return []
+      }
+      return this.user.realm_access.roles
+    },
   }
 }
 </script>
diff --git a/dbrepo-ui/composables/axios-instance.ts b/dbrepo-ui/composables/axios-instance.ts
index d4f274b717e938c3305bc20463c93d9e9474e079..55799aa741ca48fddca66830ee192daa1231ad36 100644
--- a/dbrepo-ui/composables/axios-instance.ts
+++ b/dbrepo-ui/composables/axios-instance.ts
@@ -1,11 +1,10 @@
 import axios, {type AxiosInstance} from 'axios'
-import {useUserStore} from '@/stores/user'
 
 let instance: AxiosInstance | null = null;
 
 export const useAxiosInstance = () => {
-  const config = useRuntimeConfig();
   const userStore = useUserStore()
+  const config = useRuntimeConfig();
   if (!instance) {
     instance = axios.create({
       timeout: 90_000,
@@ -19,37 +18,8 @@ export const useAxiosInstance = () => {
     });
     instance.interceptors.request.use((config) => {
       const token = userStore.getToken
-      const refreshToken = userStore.getRefreshToken
-      if (!token || !refreshToken) {
-        return config
-      }
-      const authenticationService = useAuthenticationService()
-      if (authenticationService.isExpiredToken(refreshToken)) {
-        console.warn('Refresh token is expired: trigger logout of user')
-        userStore.logout()
-        return config
-      }
-      if (!authenticationService.isExpiredToken(token)) {
-        config.headers.Authorization = `Bearer ${token}`
-        return config
-      }
-      console.warn('Access token expired: request a new one')
-      const userService = useUserService()
-      return userService.refreshToken(refreshToken)
-        .then((response: KeycloakOpenIdTokenDto) => {
-          userStore.setToken(response.access_token)
-          userStore.setRefreshToken(response.refresh_token)
-          console.debug('new access token expires:', authenticationService.tokenToExpiryDate(response.access_token))
-          config.headers.Authorization = `Bearer ${response.access_token}`
-          return config
-        })
-        .catch((error: ApiErrorDto) => {
-          if (error.code === 'error.user.credentials') {
-            console.warn('User session expired.')
-            userStore.logout()
-          }
-          return config
-        });
+      config.headers.Authorization = `Bearer ${token}`
+      return config
     })
   }
   return instance;
diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue
index fb4c40266b6dcc1f6f15a340cd400b8d4a0b3cd4..c3bd070422bb488f880d9a891acabfe22c230443 100644
--- a/dbrepo-ui/layouts/default.vue
+++ b/dbrepo-ui/layouts/default.vue
@@ -90,16 +90,16 @@
           @click:append-inner="retrieve" />
         <v-spacer />
         <v-btn
-          v-if="!user"
+          v-if="!$oidc.isLoggedIn"
           class="mr-2"
           color="secondary"
           variant="flat"
           prepend-icon="mdi-login"
-          to="/login">
+          @click="$oidc.login()">
           {{ $t('navigation.login') }}
         </v-btn>
         <v-btn
-          v-if="!user"
+          v-if="!$oidc.isLoggedIn"
           color="primary"
           variant="flat"
           prepend-icon="mdi-account-plus"
@@ -107,12 +107,12 @@
           {{ $t('navigation.signup') }}
         </v-btn>
         <v-btn
-          v-if="user"
+          v-if="$oidc.user"
           to="/user"
           variant="plain"
-          :text="user.username" />
+          :text="$oidc.user.preferred_username" />
         <v-menu
-          v-if="user"
+          v-if="$oidc.isLoggedIn"
           location="bottom">
           <template v-slot:activator="{ props }">
             <v-btn
@@ -121,20 +121,19 @@
           </template>
           <v-list>
             <v-list-item
-              v-if="user"
+              v-if="$oidc.user"
               exact
-              :to="`/search?type=database&owner.username=${user.username}`">
+              :to="`/search?type=database&owner.username=${$oidc.user.username}`">
               {{ $t('navigation.databases') + ' ' + $t('navigation.mine')}}
             </v-list-item>
             <v-list-item
-              v-if="user"
+              v-if="$oidc.user"
               exact
-              :to="`/search?type=identifier&identifiers.creator.username=${user.username}`">
+              :to="`/search?type=identifier&identifiers.creator.username=${$oidc.user.username}`">
               {{ $t('navigation.identifiers') + ' ' + $t('navigation.mine') }}
             </v-list-item>
             <v-list-item
-              v-if="user"
-              @click="logout">
+              @click="logout()">
               {{ $t('navigation.logout') }}
             </v-list-item>
           </v-list>
@@ -144,12 +143,17 @@
     <v-main>
       <v-container>
         <slot />
+        <pre>{{ accessToken }}</pre>
+        <pre>$oidc.isLoggedIn={{ $oidc.isLoggedIn }}</pre>
+        <pre v-if="$oidc.isLoggedIn" style="background:green;">$oidc.user={{$oidc.user}}</pre>
       </v-container>
     </v-main>
   </v-app>
 </template>
 
 <script setup>
+const { $oidc } = useNuxtApp()
+const accessToken = useCookie('oidc._access_token')
 const config = useRuntimeConfig()
 useServerHead({
   title: config.public.title
@@ -158,7 +162,6 @@ useServerHead({
 <script>
 import { useUserStore } from '@/stores/user'
 import { useCacheStore } from '@/stores/cache'
-
 export default {
   data () {
     return {
@@ -172,31 +175,20 @@ export default {
       loadingSearch: false,
       loadingDatabases: false,
       search: null,
-      userStore: useUserStore(),
-      cacheStore: useCacheStore()
     }
   },
   computed: {
-    token () {
-      return this.userStore.getToken
-    },
-    user () {
-      return this.userStore.getUser
-    },
-    locale () {
-      return this.userStore.getLocale
-    },
     messages () {
-      return this.cacheStore.getMessages
+      const cacheStore = useCacheStore()
+      return cacheStore.getMessages
     },
     table () {
-      return this.cacheStore.getTable
+      const cacheStore = useCacheStore()
+      return cacheStore.getTable
     },
     database () {
-      return this.cacheStore.getDatabase
-    },
-    roles () {
-      return this.userStore.getRoles
+      const cacheStore = useCacheStore()
+      return cacheStore.getDatabase
     },
     version () {
       return this.$config.public.version
@@ -231,22 +223,23 @@ export default {
           return
         }
         /* load database and optional access */
+        const cacheStore = useCacheStore()
         this.cacheStore.setRouteDatabase(newObj.database_id)
         if (this.user) {
-          this.userStore.setRouteAccess(newObj.database_id)
+          const userStore = useUserStore()
+          userStore.setRouteAccess(newObj.database_id)
         }
         if (!newObj.table_id) {
           return
         }
         /* load table */
-        this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id)
+        cacheStore.setRouteTable(newObj.database_id, newObj.table_id)
       },
       deep: true,
       immediate: true
     }
   },
   mounted () {
-    this.initEnvironment()
     if (this.$route.query && this.$route.query.q) {
       this.search = this.$route.query.q
     }
@@ -254,7 +247,8 @@ export default {
       return
     }
     this.setTheme()
-    this.cacheStore.reloadMessages()
+    const cacheStore = useCacheStore()
+    cacheStore.reloadMessages()
   },
   methods: {
     login () {
@@ -263,23 +257,12 @@ export default {
     },
     logout () {
       this.$vuetify.theme.global.name = 'tuwThemeLight'
-      this.userStore.logout()
-      this.$router.push('/database')
+      this.$oidc.logout()
     },
     retrieve () {
       console.debug('performing fuzzy search')
       this.$router.push({ path: '/search', query: { q: this.search } })
     },
-    initEnvironment () {
-      if (this.token && !this.user) {
-        console.error('Something went wrong with loading the user: reset user cache')
-        this.userStore.logout()
-      }
-      if (!this.locale) {
-        this.userStore.setLocale('en')
-      }
-      this.$i18n.locale = this.locale
-    },
     setTheme () {
       switch (this.user.attributes.theme) {
         case 'dark':
@@ -295,10 +278,6 @@ export default {
           this.$vuetify.theme.global.name = 'tuwThemeDarkContrast'
           break
       }
-    },
-    setLocale (code) {
-      this.userStore.setLocale(code)
-      this.$i18n.locale = this.locale
     }
   }
 }
diff --git a/dbrepo-ui/middleware/auth.ts b/dbrepo-ui/middleware/auth.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b16dfb5bbf48432aff6a191f65f962fe8d76920f
--- /dev/null
+++ b/dbrepo-ui/middleware/auth.ts
@@ -0,0 +1,7 @@
+export default defineNuxtRouteMiddleware((to, from) => {
+  if (import.meta.server) { return }
+  const { $oidc } = useNuxtApp()
+  if (!$oidc.isLoggedIn) {
+    $oidc.login(to.fullPath)
+  }
+})
diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts
index dc65c7ac3aca83cc1493bf69d20447273219c677..6b5d1a622b13f5b3cfd44a991a5d00be18c237d0 100644
--- a/dbrepo-ui/nuxt.config.ts
+++ b/dbrepo-ui/nuxt.config.ts
@@ -1,6 +1,7 @@
-import { transformAssetUrls } from 'vite-plugin-vuetify'
+import {transformAssetUrls} from 'vite-plugin-vuetify'
+
+const proxy: any = {}
 
-const proxy : any = {}
 
 /* proxies the backend calls, >>NOT<< the frontend calls (clicking) */
 if (process.env.NODE_ENV === 'development') {
@@ -19,153 +20,203 @@ if (process.env.NODE_ENV === 'development') {
 /**
  * https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering
  */
-const routeRules = {
-}
+const routeRules = {}
 
 export default defineNuxtConfig({
- app: {
-   head: {
-     charset: 'utf-8',
-     viewport: 'width=device-width, initial-scale=1',
-     meta: [
-       { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' }
-     ],
-     htmlAttrs: {
-       lang: 'en-US'
-     }
-   }
- },
-
- build: {
-   transpile: ['vuetify'],
- },
-
- css: [
-   'vuetify/lib/styles/main.sass',
-   '@mdi/font/css/materialdesignicons.min.css',
-   '@/assets/globals.css',
-   '@/assets/overrides.css',
- ],
-
- runtimeConfig: {
-   public: {
-     commit: '',
-     title: 'Database Repository',
-     logo: '/logo.svg',
-     icon: '/favicon.ico',
-     touch: '/apple-touch-icon.png',
-     version: 'bun-dev',
-     broker: {
-       host: 'localhost',
-       port: {
-         '5672': false
-       },
-       extra: ''
-     },
-     variant: {
-       input: {
-         normal: 'underlined',
-         contrast: 'outlined',
-       },
-       button: {
-         normal: 'flat',
-         contrast: 'outlined',
-       },
-       list: {
-         normal: '',
-         contrast: 'flat',
-       }
-     },
-     api: {
-       client: 'http://localhost',
-       server: 'http://gateway-service',
-     },
-     upload: {
-       client: 'http://localhost/api/upload/files'
-     },
-     database: {
-       unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--',
-       image: {
-         width: 200,
-         height: 200
-       },
-       extra: ''
-     },
-     pid: {
-       default: {
-         publisher: 'Example University'
-       }
-     },
-     doi: {
-       enabled: false,
-       endpoint: 'https://doi.org'
-     },
-     links: {
-       rabbitmq: {
-         text: 'RabbitMQ Admin',
-         href: '/admin/broker/'
-       },
-       keycloak: {
-         text: 'Keycloak Admin',
-         href: '/api/auth/'
-       }
-     }
-   }
- },
-
- routeRules,
-
- devServer: {
-   port: 3001
- },
-
- modules: [
-   '@pinia/nuxt',
-   '@pinia-plugin-persistedstate/nuxt',
-   '@nuxtjs/i18n'
- ],
-
- pinia: {
-   storesDirs: ['./stores/**'],
- },
-
- piniaPersistedstate: {
-   storage: 'localStorage'
- },
-
- i18n: {
-   lazy: false,
-   langDir: 'locales',
-   strategy: 'no_prefix',
-   defaultLocale: 'de',
-   locales: [
-     {
-       'code': 'en',
-       'file': 'en-US.json',
-       'name': 'English (US)',
-       'iso': 'en-US'
-     },
-     {
-       'code': 'de',
-       'file': 'de-AT.json',
-       'name': 'German (AT)',
-       'iso': 'de-AT'
-     }
-   ]
-
- },
-
- vite: {
-   server: {
-     proxy
-   },
-   vue: {
-     template: {
-       transformAssetUrls,
-     },
-   },
- },
-
- devtools: { enabled: true },
- compatibilityDate: '2024-07-24'
+  app: {
+    head: {
+      charset: 'utf-8',
+      viewport: 'width=device-width, initial-scale=1',
+      meta: [
+        {'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests'}
+      ],
+      htmlAttrs: {
+        lang: 'en-US'
+      }
+    }
+  },
+
+  openidConnect: {
+    addPlugin: true,
+    op: {
+      issuer: 'http://localhost/api/auth/realms/dbrepo',
+      clientId: 'dbrepo-client',
+      clientSecret: 'MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG',
+      scope: [
+        'openid',
+        'roles',
+        'attributes'
+      ]
+    },
+    config: {
+      debug: false,
+      response_type: 'code',
+      secret: 'oidc._sessionid',
+      isCookieUserInfo: false, // whether save userinfo into cookie.
+      cookie: { loginName: '' },
+      cookiePrefix: 'oidc._',
+      cookieEncrypt: true,
+      cookieEncryptKey: 'bfnuxt9c2470cb477d907b1e0917oidc',
+      cookieEncryptIV: 'ab83667c72eec9e4',
+      cookieEncryptALGO: 'aes-256-cbc',
+      cookieMaxAge: 24 * 60 * 60, //  default one day
+      hasCookieRefreshExpireDate: false, // Set this to true if your provider has an refresh_expires_in date for the refresh token
+      cookieRefreshDefaultMaxAge: 24 * 60 * 60, //  default one day if the hasCookieRefreshExpireDate is false
+      cookieFlags: {
+        access_token: {
+          httpOnly: true,
+          secure: false
+        }
+      }
+    }
+  },
+
+  build: {
+    transpile: ['vuetify'],
+  },
+
+  css: [
+    'vuetify/lib/styles/main.sass',
+    '@mdi/font/css/materialdesignicons.min.css',
+    '@/assets/globals.css',
+    '@/assets/overrides.css',
+  ],
+
+  runtimeConfig: {
+    openidConnect: {
+      op: {
+        issuer: '',
+        clientId: '',
+        clientSecret: '',
+      },
+      config: {
+        cookieFlags: {
+          access_token: {
+            httpOnly: true,
+            secure: false
+          }
+        }
+      }
+    },
+    public: {
+      commit: '',
+      title: 'Database Repository',
+      logo: '/logo.svg',
+      icon: '/favicon.ico',
+      touch: '/apple-touch-icon.png',
+      version: 'bun-dev',
+      broker: {
+        host: 'localhost',
+        port: {
+          '5672': false
+        },
+        extra: ''
+      },
+      variant: {
+        input: {
+          normal: 'underlined',
+          contrast: 'outlined',
+        },
+        button: {
+          normal: 'flat',
+          contrast: 'outlined',
+        },
+        list: {
+          normal: '',
+          contrast: 'flat',
+        }
+      },
+      api: {
+        client: 'http://localhost',
+        server: 'http://gateway-service',
+      },
+      upload: {
+        client: 'http://localhost/api/upload/files'
+      },
+      database: {
+        unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--',
+        image: {
+          width: 200,
+          height: 200
+        },
+        extra: ''
+      },
+      pid: {
+        default: {
+          publisher: 'Example University'
+        }
+      },
+      doi: {
+        enabled: false,
+        endpoint: 'https://doi.org'
+      },
+      links: {
+        rabbitmq: {
+          text: 'RabbitMQ Admin',
+          href: '/admin/broker/'
+        },
+        keycloak: {
+          text: 'Keycloak Admin',
+          href: '/api/auth/'
+        }
+      }
+    }
+  },
+
+  routeRules,
+
+  devServer: {
+    port: 3001
+  },
+
+  modules: [
+    '@pinia/nuxt',
+    '@pinia-plugin-persistedstate/nuxt',
+    '@nuxtjs/i18n',
+    'nuxt-openid-connect'
+  ],
+
+  pinia: {
+    storesDirs: ['./stores/**'],
+  },
+
+  piniaPersistedstate: {
+    storage: 'localStorage'
+  },
+
+  i18n: {
+    lazy: false,
+    langDir: 'locales',
+    strategy: 'no_prefix',
+    defaultLocale: 'de',
+    locales: [
+      {
+        'code': 'en',
+        'file': 'en-US.json',
+        'name': 'English (US)',
+        'iso': 'en-US'
+      },
+      {
+        'code': 'de',
+        'file': 'de-AT.json',
+        'name': 'German (AT)',
+        'iso': 'de-AT'
+      }
+    ]
+
+  },
+
+  vite: {
+    server: {
+      proxy
+    },
+    vue: {
+      template: {
+        transformAssetUrls,
+      },
+    },
+  },
+
+  devtools: {enabled: true},
+  compatibilityDate: '2024-07-24'
 })
diff --git a/dbrepo-ui/package.json b/dbrepo-ui/package.json
index 2bbe6696bc2e59845e4d1d30194a860923f2760d..5dfce95973ceb51b640f00439e202fec27ee3d70 100644
--- a/dbrepo-ui/package.json
+++ b/dbrepo-ui/package.json
@@ -25,6 +25,7 @@
     "merkle-json": "^2.6.0",
     "moment": "^2.30.1",
     "nuxt": "^3.10.3",
+    "nuxt-openid-connect": "^0.8.1",
     "parse-md": "^3.0.3",
     "pinia": "^2.1.7",
     "qs": "^6.11.2",
diff --git a/dbrepo-ui/pages/user/authentication.vue b/dbrepo-ui/pages/user/authentication.vue
index 4cb3e11a02c34f38cf2a5a197e10c4d33762661f..3c346a8bcebad1326c856983b7e9008d7a2afc58 100644
--- a/dbrepo-ui/pages/user/authentication.vue
+++ b/dbrepo-ui/pages/user/authentication.vue
@@ -60,6 +60,11 @@
   </div>
 </template>
 
+<script setup>
+definePageMeta({
+  middleware: ['auth']
+})
+</script>
 <script>
 import UserToolbar from '@/components/user/UserToolbar.vue'
 import { useUserStore } from '@/stores/user'
@@ -112,7 +117,7 @@ export default {
     changePassword () {
       this.loadingUpdate = true
       const userService = useUserService()
-      userService.updatePassword(this.user.id, {'password': this.password})
+      userService.updatePassword(this.user.sub, {'password': this.password})
         .then(() => {
           const toast = useToastInstance()
           toast.success(this.$t('success.user.password'))
diff --git a/dbrepo-ui/pages/user/developer.vue b/dbrepo-ui/pages/user/developer.vue
deleted file mode 100644
index 9b6f80a591a6a23b0059ba916c81a1bfdcc7737f..0000000000000000000000000000000000000000
--- a/dbrepo-ui/pages/user/developer.vue
+++ /dev/null
@@ -1,230 +0,0 @@
-<template>
-  <div>
-    <UserToolbar />
-    <v-window
-      v-model="tab">
-      <v-window-item>
-        <v-card
-          v-if="canHandleMessages"
-          :title="$t('pages.settings.subpages.developer.maintenance.title')"
-          rounded="0"
-          variant="flat">
-          <v-data-table
-            :headers="headers"
-            :items="messages"
-            :loading="loadingMessages"
-            :items-per-page="10">
-            <template v-slot:item.action="{ item }">
-              <v-btn
-                size="x-small"
-                variant="flat"
-                :text="$t('pages.settings.subpages.developer.maintenance.modify.text')"
-                @click="modifyMessage(item)" />
-            </template>
-          </v-data-table>
-          <v-card-text>
-            <v-btn
-              size="small"
-              variant="flat"
-              :text="$t('pages.settings.subpages.developer.maintenance.add.text')"
-              :disabled="!canCreateMessage"
-              @click="createMessage" />
-          </v-card-text>
-        </v-card>
-        <v-divider
-          v-if="canHandleMessages" />
-        <v-card
-          :title="$t('pages.settings.subpages.developer.token.title')"
-          :subtitle="$t('pages.settings.subpages.developer.token.subtitle')"
-          variant="flat"
-          rounded="0">
-          <v-card-text>
-            <v-row dense>
-              <v-col xl="4">
-                <v-text-field
-                  v-model="accessTokenField"
-                  disabled
-                  :variant="inputVariant"
-                  :label="$t('pages.settings.subpages.developer.token.access.label')" />
-              </v-col>
-              <v-col xl="2">
-                <v-text-field
-                  v-model="tokenExpiry"
-                  disabled
-                  :variant="inputVariant"
-                  :label="expiryLabel(token)" />
-              </v-col>
-            </v-row>
-            <v-row dense>
-              <v-col xl="4">
-                <v-text-field
-                  v-model="refreshTokenField"
-                  disabled
-                  :variant="inputVariant"
-                  :label="$t('pages.settings.subpages.developer.token.refresh.label')" />
-              </v-col>
-              <v-col xl="2">
-                <v-text-field
-                  v-model="refreshTokenExpiry"
-                  disabled
-                  :variant="inputVariant"
-                  :label="expiryLabel(refreshToken)" />
-              </v-col>
-            </v-row>
-          </v-card-text>
-        </v-card>
-      </v-window-item>
-    </v-window>
-    <v-breadcrumbs :items="items" class="pa-0 mt-2" />
-    <v-dialog
-      v-model="dialog"
-      persistent
-      max-width="640">
-      <EditMaintenanceMessage
-        :id="messageId"
-        @close-dialog="closeDialog" />
-    </v-dialog>
-  </div>
-</template>
-
-<script>
-import UserToolbar from '@/components/user/UserToolbar.vue'
-import EditMaintenanceMessage from '@/components/dialogs/EditMaintenanceMessage.vue'
-import { formatTimestampUTCLabel, isActiveMessage, timestampsToHumanDifference } from '@/utils'
-import { useUserStore } from '@/stores/user'
-import { useCacheStore } from '@/stores/cache'
-
-export default {
-  components: {
-    UserToolbar,
-    EditMaintenanceMessage
-  },
-  data () {
-    return {
-      tab: 0,
-      accessTokenField: null,
-      refreshTokenField: null,
-      headers: [
-        { title: this.$t('pages.settings.subpages.developer.maintenance.active'), value: 'active' },
-        { title: this.$t('pages.settings.subpages.developer.maintenance.type'), value: 'type' },
-        { title: this.$t('pages.settings.subpages.developer.maintenance.message'), value: 'message' },
-        { title: this.$t('pages.settings.subpages.developer.maintenance.action'), value: 'action' }
-      ],
-      items: [
-        {
-          title: this.$t('navigation.user'),
-          to: '/user'
-        },
-        {
-          title: this.$t('toolbars.user.developer'),
-          to: `/user/developer`,
-          disabled: true
-        }
-      ],
-      messages: [],
-      loadingMessages: false,
-      dialog: false,
-      messageId: null,
-      userStore: useUserStore(),
-      cacheStore: useCacheStore()
-    }
-  },
-  computed: {
-    token () {
-      return this.userStore.getToken
-    },
-    tokenExpiry () {
-      if (!this.token) {
-        return null
-      }
-      const authenticationService = useAuthenticationService()
-      return formatTimestampUTCLabel(authenticationService.tokenToExpiryDate(this.token))
-    },
-    refreshToken () {
-      return this.userStore.getRefreshToken
-    },
-    refreshTokenExpiry () {
-      if (!this.refreshToken) {
-        return null
-      }
-      const authenticationService = useAuthenticationService()
-      return formatTimestampUTCLabel(authenticationService.tokenToExpiryDate(this.refreshToken))
-    },
-    user () {
-      return this.userStore.getUser
-    },
-    roles () {
-      return this.userStore.getRoles
-    },
-    canCreateMessage () {
-      if (!this.roles) {
-        return false
-      }
-      return this.roles.includes('create-maintenance-message')
-    },
-    canModifyMessage () {
-      if (!this.roles) {
-        return false
-      }
-      return this.roles.includes('modify-maintenance-message')
-    },
-    canHandleMessages () {
-      return this.canCreateMessage || this.canModifyMessage
-    },
-    inputVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal
-    },
-    buttonVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
-    }
-  },
-  mounted () {
-    this.loadMessages()
-    if (!this.token || !this.refreshToken) {
-      return
-    }
-    this.accessTokenField = this.token
-    this.refreshTokenField = this.refreshToken
-  },
-  methods: {
-    submit () {
-    },
-    modifyMessage (message) {
-      this.messageId = message.id
-      this.dialog = true
-    },
-    createMessage () {
-      this.messageId = null
-      this.dialog = true
-    },
-    expiryLabel (token) {
-      const authenticationService = useAuthenticationService()
-      return this.$t('pages.settings.subpages.developer.token.expiry') + ' ' + timestampsToHumanDifference(Date.now(), authenticationService.tokenToExpiryDate(token))
-    },
-    loadMessages () {
-      const messageService = useMessageService()
-      messageService.findAll()
-        .then((messages) => {
-          this.messages = messages.map((message) => {
-            message.active = isActiveMessage(message) ? '● true' : 'false'
-            return message
-          })
-        })
-        .catch(() => {
-          this.loadingMessages = false
-        })
-        .finally(() => {
-          this.loadingMessages = false
-        })
-    },
-    closeDialog (event) {
-      if (event.success) {
-        this.cacheStore.reloadMessages()
-      }
-      this.dialog = false
-    }
-  }
-}
-</script>
diff --git a/dbrepo-ui/pages/user/index.vue b/dbrepo-ui/pages/user/index.vue
index e729d9086f2be6604c2b9d0222c00acc79297f85..d6691d8302de92dcd23b060d79163df82b9c24e2 100644
--- a/dbrepo-ui/pages/user/index.vue
+++ b/dbrepo-ui/pages/user/index.vue
@@ -2,27 +2,14 @@
   <div />
 </template>
 
+<script setup>
+definePageMeta({
+  middleware: ['auth']
+})
+</script>
 <script>
-import { useUserStore } from '@/stores/user'
-
 export default {
-  data () {
-    return {
-      userStore: useUserStore()
-    }
-  },
-  computed: {
-    token () {
-      return this.userStore.getToken
-    },
-    user () {
-      return this.userStore.getUser
-    }
-  },
   mounted () {
-    if (!this.user) {
-      return
-    }
     this.$router.push('/user/info')
   }
 }
diff --git a/dbrepo-ui/pages/user/info.vue b/dbrepo-ui/pages/user/info.vue
index 3501818ca0408474e81a9038f15a84d61fb662ff..cb0e3fd9228477ade85e20f343a2a564a7c34701 100644
--- a/dbrepo-ui/pages/user/info.vue
+++ b/dbrepo-ui/pages/user/info.vue
@@ -13,18 +13,17 @@
               <v-row dense>
                 <v-col md="6">
                   <v-text-field
-                    v-model="model.id"
+                    v-model="user.sub"
                     readonly
+                    disabled
                     :variant="inputVariant"
-                    :label="$t('pages.user.subpages.info.id.label')"
-                    append-inner-icon="mdi-content-copy"
-                    @click:append-inner="copy" />
+                    :label="$t('pages.user.subpages.info.id.label')" />
                 </v-col>
               </v-row>
               <v-row dense>
                 <v-col md="6">
                   <v-text-field
-                    v-model="model.username"
+                    v-model="user.preferred_username"
                     disabled
                     :variant="inputVariant"
                     :label="$t('pages.user.subpages.info.username.label')"  />
@@ -124,6 +123,12 @@
   </div>
 </template>
 
+<script setup>
+definePageMeta({
+  middleware: ['auth']
+})
+const { $oidc } = useNuxtApp()
+</script>
 <script>
 import UserToolbar from '@/components/user/UserToolbar.vue'
 import { useUserStore } from '@/stores/user'
@@ -143,7 +148,6 @@ export default {
       theme: null,
       orcidLoading: false,
       model: {
-        id: null,
         username: null,
         firstname: null,
         lastname: null,
@@ -176,10 +180,13 @@ export default {
   },
   computed: {
     user () {
-      return this.userStore.getUser
+      return this.$oidc.user
     },
     roles () {
-      return this.userStore.getRoles
+      if (!this.user || !this.user.realm_access) {
+        return []
+      }
+      return this.user.realm_access.roles
     },
     locale () {
       return this.userStore.getLocale
@@ -216,14 +223,12 @@ export default {
         language: this.model.language,
       }
       const userService = useUserService()
-      userService.update(this.user.id, payload)
+      userService.update(this.user.sub, payload)
         .then((user) => {
           console.info('Updated user information')
           const toast = useToastInstance()
           toast.success(this.$t('success.user.info'))
-          this.userStore.setUser(user)
           /* language */
-          this.userStore.setLocale(this.model.language)
           this.$i18n.locale = this.locale
           /* theme */
           switch (this.model.theme) {
@@ -253,14 +258,12 @@ export default {
         return
       }
       this.model = {
-        id: this.user.id,
-        username: this.user.username,
         firstname: this.user.given_name,
         lastname: this.user.family_name,
-        orcid: this.user.attributes.orcid,
-        affiliation: this.user.attributes.affiliation,
-        theme: this.user.attributes.theme,
-        language: this.user.attributes.language
+        orcid: this.user.attributes?.orcid,
+        affiliation: this.user.attributes?.affiliation,
+        theme: this.user.attributes?.theme,
+        language: this.user.attributes?.language
       }
     },
     retrieve () {
@@ -284,11 +287,6 @@ export default {
         .finally(() => {
           this.orcidLoading = false
         })
-    },
-    copy () {
-      navigator.clipboard.writeText(this.model.id)
-      const toast = useToastInstance()
-      toast.success(this.$t('success.clipboard.user'))
     }
   }
 }
diff --git a/dbrepo-ui/stores/user.js b/dbrepo-ui/stores/user.js
index 522ce02a06ed1c695897d92a73c2682d791b499a..e857a0681ef999b2ff15bc9ef72d6b8b63091bcb 100644
--- a/dbrepo-ui/stores/user.js
+++ b/dbrepo-ui/stores/user.js
@@ -4,50 +4,22 @@ export const useUserStore = defineStore('user', {
   persist: true,
   state: () => {
     return {
-      /** @type String */
-      token: null,
-      /** @type String */
-      refreshToken: null,
-      roles: [],
-      user: null,
       access: null,
-      locale: null
+      /** @type String */
+      token: null
     }
   },
   getters: {
     getToken: (state) => state.token,
-    getRefreshToken: (state) => state.refreshToken,
-    getRoles: (state) => state.roles,
-    getUser: (state) => state.user,
     getAccess: (state) => state.access,
-    getLocale: (state) => state.locale
   },
   actions: {
     setToken(token) {
       this.token = token
     },
-    setRefreshToken(refreshToken) {
-      this.refreshToken = refreshToken
-    },
-    setRoles(roles) {
-      this.roles = roles
-    },
-    setUser(user) {
-      this.user = user
-    },
     setAccess(access) {
       this.access = access
     },
-    setLocale (locale) {
-      this.locale = locale
-    },
-    logout() {
-      this.token = null
-      this.refreshToken = null
-      this.roles = []
-      this.user = null
-      this.access = null
-    },
     setRouteAccess(databaseId) {
       if (!databaseId || !this.user || !this.user.id) {
         return