diff --git a/deploy/app.js b/deploy/app.js
index 95d712170150e7a629c19e03348df9e4aaffc022..1eeaf6a788a0076e67066abe0149b562c64d3c50 100644
--- a/deploy/app.js
+++ b/deploy/app.js
@@ -80,6 +80,11 @@ app.use((_req, res, next) => {
  */
 app.use(require('./devBanner'))
 
+/**
+ * User route, for user profile/management
+ */
+app.use('/user', require('./user'))
+
 /**
  * only use compression for production
  * this allows locally built aot to be served without errors
diff --git a/deploy/auth/hbp-oidc-v2.js b/deploy/auth/hbp-oidc-v2.js
new file mode 100644
index 0000000000000000000000000000000000000000..a49018f85a4c34d38021356989302cf445ffc176
--- /dev/null
+++ b/deploy/auth/hbp-oidc-v2.js
@@ -0,0 +1,44 @@
+const passport = require('passport')
+const { configureAuth } = require('./oidc')
+
+const HOSTNAME = process.env.HOSTNAME || 'http://localhost:3000'
+const HOST_PATHNAME = process.env.HOST_PATHNAME || ''
+const clientId = process.env.HBP_CLIENTID_V2 || 'no hbp id'
+const clientSecret = process.env.HBP_CLIENTSECRET_V2 || 'no hbp client secret'
+const discoveryUrl = 'https://iam.humanbrainproject.eu/auth/realms/hbp'
+const redirectUri = `${HOSTNAME}${HOST_PATHNAME}/hbp-oidc-v2/cb`
+const cb = (tokenset, {sub, given_name, family_name, ...rest}, done) => {
+  return done(null, {
+    id: `hbp-oidc-v2:${sub}`,
+    name: `${given_name} ${family_name}`,
+    type: `hbp-oidc-v2`,
+    tokenset,
+    rest
+  })
+}
+
+module.exports = async (app) => {
+  try {
+    const { oidcStrategy } = await configureAuth({
+      clientId,
+      clientSecret,
+      discoveryUrl,
+      redirectUri,
+      cb,
+      scope: 'openid email offline_access profile collab.drive',
+      clientConfig: {
+        redirect_uris: [ redirectUri ],
+        response_types: [ 'code' ]
+      }
+    })
+    
+    passport.use('hbp-oidc-v2', oidcStrategy)
+    app.get('/hbp-oidc-v2/auth', passport.authenticate('hbp-oidc-v2'))
+    app.get('/hbp-oidc-v2/cb', passport.authenticate('hbp-oidc-v2', {
+      successRedirect: `${HOST_PATHNAME}/`,
+      failureRedirect: `${HOST_PATHNAME}/`
+    }))
+  } catch (e) {
+    console.error(e)
+  }
+}
diff --git a/deploy/auth/index.js b/deploy/auth/index.js
index 0ad0941dfe9804a442d131130410d6a4098acaea..a2e6c45640632b543fbd86190c4c54956a51c9b8 100644
--- a/deploy/auth/index.js
+++ b/deploy/auth/index.js
@@ -1,4 +1,5 @@
 const hbpOidc = require('./hbp-oidc')
+const hbpOidc2 = require('./hbp-oidc-v2')
 const passport = require('passport')
 const objStoreDb = new Map()
 const HOST_PATHNAME = process.env.HOST_PATHNAME || ''
@@ -20,14 +21,7 @@ module.exports = async (app) => {
   })
 
   await hbpOidc(app)
-
-  app.get('/user', (req, res) => {
-    if (req.user) {
-      return res.status(200).send(JSON.stringify(req.user))
-    } else {
-      return res.status(401).end()
-    }
-  })
+  await hbpOidc2(app)
 
   app.get('/logout', (req, res) => {
     if (req.user && req.user.id) objStoreDb.delete(req.user.id)
diff --git a/deploy/package.json b/deploy/package.json
index 4bded39e142c1a46fde997ade7384073b5a35b1f..c8c7e0ecfad411ef5a7bc071a2d63ae962fcf362 100644
--- a/deploy/package.json
+++ b/deploy/package.json
@@ -18,6 +18,7 @@
     "body-parser": "^1.19.0",
     "express": "^4.16.4",
     "express-session": "^1.15.6",
+    "hbp-seafile": "0.0.6",
     "helmet-csp": "^2.8.0",
     "jwt-decode": "^2.2.0",
     "memorystore": "^1.6.1",
diff --git a/deploy/user/index.js b/deploy/user/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..9d1809c963bfa07e6f518991f567f0b4e5af4137
--- /dev/null
+++ b/deploy/user/index.js
@@ -0,0 +1,37 @@
+const router = require('express').Router()
+const { readUserData, saveUserData } = require('./store')
+const bodyParser = require('body-parser')
+
+const loggedInOnlyMiddleware = (req, res, next) => {
+  const { user } = req
+  if (!user) return res.status(401).end()
+  return next()
+}
+
+router.get('', loggedInOnlyMiddleware, (req, res) => {
+  return res.status(200).send(JSON.stringify(req.user))
+})
+
+router.get('/config', loggedInOnlyMiddleware, async (req, res) => {
+  const { user } = req
+  try{
+    const data = await readUserData(user)
+    res.status(200).json(data)
+  } catch (e){
+    console.error(e)
+    res.status(500).send(e.toString())
+  }
+})
+
+router.post('/config', loggedInOnlyMiddleware, bodyParser.json(), async (req, res) => {
+  const { user, body } = req
+  try {
+    await saveUserData(user, body)
+    res.status(200).end()
+  } catch (e) {
+    console.error(e)
+    res.status(500).send(e.toString())
+  }
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/deploy/user/store.js b/deploy/user/store.js
new file mode 100644
index 0000000000000000000000000000000000000000..93c9db2b1adce8baa58ecf4fca6bfecaecfc650c
--- /dev/null
+++ b/deploy/user/store.js
@@ -0,0 +1,68 @@
+const { Seafile } = require('hbp-seafile')
+const { Readable } = require('stream')
+
+const IAV_DIR_NAME = `interactive-atlas-viewer`
+const IAV_DIRECTORY = `/${IAV_DIR_NAME}/`
+const IAV_FILENAME = 'data.json'
+
+const getNewSeafilehandle = async ({ accessToken }) => {
+  const seafileHandle = new Seafile({ accessToken })
+  await seafileHandle.init()
+  return seafileHandle
+}
+
+const saveUserData = async (user, data) => {
+  const { access_token } = user && user.tokenset || {}
+  if (!access_token) throw new Error(`user or user.tokenset not set can only save logged in user data`)
+
+  let handle = await getNewSeafilehandle({ accessToken: access_token })
+
+  const s = await handle.ls()
+  const found = s.find(({ type, name }) => type === 'dir' && name === IAV_DIR_NAME)
+
+  // if dir exists, check permission. throw if no writable or readable permission
+  if (found && !/w/.test(found.permission) && !/r/.test(found.permission)){
+    throw new Error(`Writing to file not permitted. Current permission: ${found.permission}`)
+  }
+
+  // create new dir if does not exist. Should have rw permission
+  if (!found) {
+    await handle.mkdir({ dir: IAV_DIR_NAME })
+  }
+
+  const fileLs = await handle.ls({ dir: IAV_DIRECTORY })
+  const fileFound = fileLs.find(({ type, name }) => type === 'file' && name === IAV_FILENAME )
+
+  const rStream = new Readable()
+  rStream.path = IAV_FILENAME
+  rStream.push(JSON.stringify(data))
+  rStream.push(null)
+
+  if(!fileFound) {
+    return handle.uploadFile({ readStream: rStream, filename: `${IAV_FILENAME}` }, { dir: IAV_DIRECTORY })
+  }
+
+  if (fileFound && !/w/.test(fileFound.permission)) {
+    return new Error('file permission cannot be written')
+  }
+
+  return handle.updateFile({ dir: IAV_DIRECTORY, replaceFilepath: `${IAV_DIRECTORY}${IAV_FILENAME}` }, { readStream: rStream, filename: IAV_FILENAME })
+}
+
+const readUserData = async (user) => {
+  const { access_token } = user && user.tokenset || {}
+  if (!access_token) throw new Error(`user or user.tokenset not set can only save logged in user data`)
+
+  let handle = await getNewSeafilehandle({ accessToken: access_token })
+  try {
+    const r = await handle.readFile({ dir: `${IAV_DIRECTORY}${IAV_FILENAME}` })
+    return JSON.parse(r)
+  }catch(e){
+    return {}
+  }
+}
+
+module.exports = {
+  saveUserData,
+  readUserData
+}
diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts
index 81add5950f138e5be60b233086dc4f0ddf9dd9b8..fdec1e6ef5ec008b4d4bb01f8a3897ff4c482934 100644
--- a/src/services/auth.service.ts
+++ b/src/services/auth.service.ts
@@ -19,7 +19,10 @@ export class AuthService implements OnDestroy {
    */
   public loginMethods: IAuthMethod[] = [{
     name: 'HBP OIDC',
-    href: 'hbp-oidc/auth',
+    href: 'hbp-oidc/auth'
+  }, {
+    name: 'HBP OIDC v2 (beta)',
+    href: 'hbp-oidc-v2/auth'
   }]
 
   constructor(private httpClient: HttpClient) {
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
index 53fe04a19c044f2bf9a02a03db8edfd8a051fa1e..1e93b4ee8e44bba5aec691c20fde79f87165a301 100644
--- a/src/services/state/ngViewerState.store.ts
+++ b/src/services/state/ngViewerState.store.ts
@@ -1,11 +1,11 @@
 import { Injectable, OnDestroy } from '@angular/core';
-import { Actions, Effect, ofType } from '@ngrx/effects';
-import { Action, select, Store } from '@ngrx/store'
-import { combineLatest, fromEvent, Observable, Subscription } from 'rxjs';
-import { distinctUntilChanged, filter, map, mapTo, scan, shareReplay, withLatestFrom } from 'rxjs/operators';
+import { Observable, combineLatest, fromEvent, Subscription, from, of } from 'rxjs';
+import { Effect, Actions, ofType } from '@ngrx/effects';
+import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo, debounceTime, catchError, skip, throttleTime } from 'rxjs/operators';
 import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service';
-import { getNgIds, IavRootStoreInterface } from '../stateStore.service';
 import { SNACKBAR_MESSAGE } from './uiState.store';
+import { getNgIds, IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.service';
+import { Action, select, Store } from '@ngrx/store'
 
 export const FOUR_PANEL = 'FOUR_PANEL'
 export const V_ONE_THREE = 'V_ONE_THREE'
@@ -132,14 +132,72 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Stat
       ...prevState,
       forceShowSegment : action.forceShowSegment,
     }
-  case NEHUBA_READY: {
-    const { nehubaReady } = action
-    return {
-      ...prevState,
-      nehubaReady,
-    }
-  }
-  default: return prevState
+    case ADD_NG_LAYER:
+      return {
+        ...prevState,
+
+        /* this configration hides the layer if a non mixable layer already present */
+
+        /* this configuration does not the addition of multiple non mixable layers */
+        // layers : action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
+        //   ? prevState.layers
+        //   : prevState.layers.concat(action.layer)
+
+        /* this configuration allows the addition of multiple non mixables */
+        // layers : prevState.layers.map(l => mapLayer(l, action.layer)).concat(action.layer)
+        layers : mixNgLayers(prevState.layers, action.layer) 
+        
+        // action.layer.constructor === Array 
+        //   ? prevState.layers.concat(action.layer)
+        //   : prevState.layers.concat({
+        //     ...action.layer,
+        //     ...( action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
+        //           ? {visible: false}
+        //           : {})
+        //   })
+      } 
+    case REMOVE_NG_LAYERS:
+      const { layers } = action
+      const layerNameSet = new Set(layers.map(l => l.name))
+      return {
+        ...prevState,
+        layers: prevState.layers.filter(l => !layerNameSet.has(l.name))
+      }
+    case REMOVE_NG_LAYER:
+      return {
+        ...prevState,
+        layers : prevState.layers.filter(l => l.name !== action.layer.name)
+      }
+    case SHOW_NG_LAYER:
+      return {
+        ...prevState,
+        layers : prevState.layers.map(l => l.name === action.layer.name
+          ? { ...l, visible: true }
+          : l)
+      }
+    case HIDE_NG_LAYER:
+      return {
+        ...prevState,
+
+        layers : prevState.layers.map(l => l.name === action.layer.name
+          ? { ...l, visible: false }
+          : l)
+      }
+    case FORCE_SHOW_SEGMENT:
+      return {
+        ...prevState,
+        forceShowSegment : action.forceShowSegment
+      }
+    case NEHUBA_READY: 
+      const { nehubaReady } = action
+      return {
+        ...prevState,
+        nehubaReady
+      }
+    case GENERAL_ACTION_TYPES.APPLY_STATE:
+      const { ngViewerState } = (action as any).state
+      return ngViewerState
+    default: return prevState
   }
 }
 
@@ -187,11 +245,54 @@ export class NgViewerUseEffect implements OnDestroy {
 
   private subscriptions: Subscription[] = []
 
+  @Effect()
+  public applySavedUserConfig$: Observable<any>
+
   constructor(
     private actions: Actions,
     private store$: Store<IavRootStoreInterface>,
-    private constantService: AtlasViewerConstantsServices,
-  ) {
+    private constantService: AtlasViewerConstantsServices
+  ){
+
+    // TODO either split backend user to be more granular, or combine the user config into a single subscription
+    this.subscriptions.push(
+      this.store$.pipe(
+        select('ngViewerState'),
+        distinctUntilChanged(),
+        debounceTime(200),
+        skip(1),
+        // Max frequency save once every second
+        throttleTime(1000)
+      ).subscribe(({panelMode, panelOrder}) => {
+        fetch(`${this.constantService.backendUrl}user/config`, {
+          method: 'POST',
+          headers: {
+            'Content-type': 'application/json'
+          },
+          body: JSON.stringify({ ngViewerState: { panelMode, panelOrder } })
+        })
+      })
+    )
+
+    this.applySavedUserConfig$ = from(fetch(`${this.constantService.backendUrl}user/config`).then(r => r.json())).pipe(
+      catchError((err,caught) => of(null)),
+      filter(v => !!v),
+      withLatestFrom(this.store$),
+      map(([{ngViewerState: fetchedNgViewerState}, state]) => {
+        const { ngViewerState } = state
+        return {
+          type: GENERAL_ACTION_TYPES.APPLY_STATE,
+          state: {
+            ...state,
+            ngViewerState: {
+              ...ngViewerState,
+              ...fetchedNgViewerState
+            }
+          }
+        }
+      })
+    )
+
     const toggleMaxmimise$ = this.actions.pipe(
       ofType(ACTION_TYPES.TOGGLE_MAXIMISE),
       shareReplay(1),
diff --git a/src/ui/signinModal/signinModal.style.css b/src/ui/signinModal/signinModal.style.css
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cbbec1ebb685a7f543a0e476c8eff9401befb834 100644
--- a/src/ui/signinModal/signinModal.style.css
+++ b/src/ui/signinModal/signinModal.style.css
@@ -0,0 +1,5 @@
+a
+{
+  display:inline-block;
+  margin-top: 0.25rem;
+}
\ No newline at end of file
diff --git a/src/ui/signinModal/signinModal.template.html b/src/ui/signinModal/signinModal.template.html
index e2ab8fe9f88393075d7597edaec26356b2d1b309..faac23115ca6575f9497a2ae2805d8ce7a061968 100644
--- a/src/ui/signinModal/signinModal.template.html
+++ b/src/ui/signinModal/signinModal.template.html
@@ -12,9 +12,6 @@
 </div>
 
 <ng-template #notLoggedIn>
-  <span>
-    Login via
-  </span>
   <a *ngFor="let m of loginMethods"
     [href]="m.href">
     <button