diff --git a/src/App.js b/src/App.js
index 8376431b189bcd3447684f2c77c62162d9e5549f..a4b12a20aa65c30dc526b5566749710be34aa599 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,6 +1,6 @@
 import React from 'react';
 
-import { HashRouter, Switch, Route } from 'react-router-dom';
+import { BrowserRouter, Switch, Route } from 'react-router-dom';
 
 import EntryPage from './components/entry-page/entry-page.js';
 import ErrorDialog from './components/dialog/error-dialog.js';
@@ -13,7 +13,7 @@ class App extends React.Component {
       <div>
         <ErrorDialog />
         <NotificationDialog/>
-        <HashRouter>
+        <BrowserRouter>
           <Switch>
             <Route path='/experiments-overview'>
               <ExperimentOverview />
@@ -22,7 +22,7 @@ class App extends React.Component {
               <EntryPage />
             </Route>
           </Switch>
-        </HashRouter>
+        </BrowserRouter>
       </div>
     );
   }
diff --git a/src/services/authentication-service-v2.js b/src/services/authentication-service-v2.js
new file mode 100644
index 0000000000000000000000000000000000000000..2548876ea73035b1bffd21a606ceb65b2f4c87e2
--- /dev/null
+++ b/src/services/authentication-service-v2.js
@@ -0,0 +1,157 @@
+import config from '../config.json';
+
+let _instance = null;
+const SINGLETON_ENFORCER = Symbol();
+
+/**
+ * Service taking care of OIDC/FS authentication for NRP accounts
+ */
+class AuthenticationService {
+  constructor(enforcer) {
+    if (enforcer !== SINGLETON_ENFORCER) {
+      throw new Error('Use ' + this.constructor.name + '.instance');
+    }
+
+    this.CLIENT_ID = config.authV2.clientId;
+    this.STORAGE_KEY = `tokens-${this.CLIENT_ID}@https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth`;
+
+    this.redirectToAuthPage = true;
+    this.checkForSessionStateAndAuthCode();
+    //this.checkForNewTokenToStore();
+  }
+
+  static get instance() {
+    if (_instance == null) {
+      _instance = new AuthenticationService(SINGLETON_ENFORCER);
+    }
+
+    return _instance;
+  }
+
+  /**
+   * Checks if the current page URL contains access tokens.
+   * This happens when the successfully logging in at the proxy login page and
+   * being redirected back with the token info.
+   * Will automatically remove additional access info and present a clean URL after being redirected.
+   */
+  checkForNewTokenToStore() {
+    const path = window.location.href;
+    const accessTokenMatch = /&access_token=([^&]*)/.exec(path);
+
+    if (!accessTokenMatch || !accessTokenMatch[1]) {
+      return;
+    }
+
+    let accessToken = accessTokenMatch[1];
+
+    localStorage.setItem(
+      this.STORAGE_KEY,
+      //eslint-disable-next-line camelcase
+      JSON.stringify([{ access_token: accessToken }])
+    );
+    const pathMinusAccessToken = path.substr(0, path.indexOf('&access_token='));
+    window.location.href = pathMinusAccessToken;
+  }
+
+  /**
+   * Checks if the current page URL contains access tokens.
+   * This happens when the successfully logging in at the proxy login page and
+   * being redirected back with the token info.
+   * Will automatically remove additional access info and present a clean URL after being redirected.
+   */
+  checkForSessionStateAndAuthCode() {
+    const path = window.location.href;
+    const sessionStateMatch = /&session_state=([^&]*)/.exec(path);
+    const authCodeMatch = /&code=([^&]*)/.exec(path);
+
+    if (!sessionStateMatch || !authCodeMatch[1]) {
+      return;
+    }
+
+    this.redirectToAuthPage = false;
+
+    let sessionState = sessionStateMatch[1];
+    let authCode = authCodeMatch[1];
+    console.info({sessionState: sessionState, authCode: authCode});
+
+    let urlRequestAccessToken = 'https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/token?'
+      + 'grant_type=authorization_code'
+      + '&client_id=' + this.CLIENT_ID
+      + '&redirect_uri=' + window.location.href
+      + '&code=' + authCode
+      + '&client_secret=' + 'some-secret';
+    let responseAccessTokenRequest = this.httpRequestPOST(urlRequestAccessToken);
+
+    /*localStorage.setItem(
+      this.STORAGE_KEY,
+      //eslint-disable-next-line camelcase
+      JSON.stringify([{ access_token: accessToken }])
+    );*/
+    //const pathMinusAccessToken = path.substr(0, path.indexOf('?'));
+    //window.location.href = pathMinusAccessToken;
+  }
+
+  /**
+   * Clear currently stored access token.
+   */
+  clearStoredToken() {
+    localStorage.removeItem(this.STORAGE_KEY);
+  }
+
+  /**
+   * Get the stored access token.
+   *
+   * @return token The stored access token. Or strings identifying 'no-token' / 'malformed-token'.
+   */
+  getStoredToken() {
+    let storedItem = localStorage.getItem(this.STORAGE_KEY);
+    if (!storedItem) {
+      // this token will be rejected by the server and the client will get a proper auth error
+      return AuthenticationService.CONSTANTS.NO_TOKEN;
+    }
+
+    try {
+      let tokens = JSON.parse(storedItem);
+      return tokens.length ? tokens[tokens.length - 1].access_token : null;
+    }
+    catch (e) {
+      // this token will be rejected by the server and the client will get a proper auth error
+      return AuthenticationService.CONSTANTS.MALFORMED_TOKEN;
+    }
+  }
+
+  /**
+   * Opens the proxy's authentication page.
+   *
+   * @param {*} url The URL of the authentication page.
+   * If not an absolute URL it is assumed to be a subpage of the proxy.
+   */
+  openAuthenticationPage(url = config.authV2.url) {
+    if (!this.redirectToAuthPage) {
+      return;
+    }
+
+    this.clearStoredToken();
+
+    let absoluteUrl = /^https?:\/\//i;
+    if (!absoluteUrl.test(url)) {
+      url = `https://${url}`;
+    }
+    /*window.location.href = `${url}
+      &client_id=${this.CLIENT_ID}
+      &redirect_uri=${encodeURIComponent(window.location.href)}`;*/
+
+    let testClientID = 'community-apps-tutorial';
+    let testRedirectURI = window.location.href; //'http://localhost:3000';
+    window.location.href = url +
+      '&client_id=' + testClientID +
+      '&redirect_uri=' + testRedirectURI;
+  }
+}
+
+AuthenticationService.CONSTANTS = Object.freeze({
+  MALFORMED_TOKEN: 'malformed-token',
+  NO_TOKEN: 'no-token'
+});
+
+export default AuthenticationService;
diff --git a/src/services/http-service.js b/src/services/http-service.js
index 28c898193548fb623208b338982bc385f825650c..30651bc6ef8efe5b3be2c6402bc6fa12610c165e 100644
--- a/src/services/http-service.js
+++ b/src/services/http-service.js
@@ -1,7 +1,7 @@
 
 import { EventEmitter } from 'events';
 
-import AuthenticationService from './authentication-service.js';
+import AuthenticationService from './authentication-service-v2.js';
 
 /**
  * Base class that performs http requests with default request options.
@@ -55,7 +55,9 @@ export class HttpService extends EventEmitter {
     if (!response.ok) {
       if (response.status === 477) {
         const responseText = await response.text();
-        AuthenticationService.instance.openAuthenticationPage(responseText);
+        console.info('auth error');
+        console.info(responseText);
+        AuthenticationService.instance.openAuthenticationPage(/*responseText*/);
       }
       else if (response.status === 478) {
         //TODO: redirect to maintenance page
diff --git a/src/services/proxy/nrp-user-service.js b/src/services/proxy/nrp-user-service.js
index 9801ef00e73d8fb9f06f8a02e86b0c82d1abb9b6..94f0024d02737c0e4bf7b0ccab61d11a74cdd3d0 100644
--- a/src/services/proxy/nrp-user-service.js
+++ b/src/services/proxy/nrp-user-service.js
@@ -61,7 +61,11 @@ class NrpUserService extends HttpService {
    */
   async getCurrentUser() {
     if (!this.currentUser) {
-      this.currentUser = await (await this.httpRequestGET(IDENTITY_ME_URL)).json();
+      let responseIdentity = await this.httpRequestGET(IDENTITY_ME_URL);
+      console.info(responseIdentity);
+      if (responseIdentity.ok) {
+        this.currentUser = await responseIdentity.json();
+      }
     }
 
     return this.currentUser;