diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/GetServerPublicInformationOperationExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/GetServerPublicInformationOperationExecutor.java
index 9b2d07595b22bb3e6aa1d1e67c3e8d3004d4393b..511c4ebf9dd97e2bd34d6d3a3c885f6dfb64aea6 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/GetServerPublicInformationOperationExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/GetServerPublicInformationOperationExecutor.java
@@ -16,6 +16,7 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.get;
 
 import java.util.Map;
+import java.util.Properties;
 import java.util.TreeMap;
 
 import javax.annotation.Resource;
@@ -51,12 +52,24 @@ public class GetServerPublicInformationOperationExecutor
     protected GetServerPublicInformationOperationResult doExecute(IOperationContext context, GetServerPublicInformationOperation operation)
     {
         Map<String, String> info = new TreeMap<>();
-        info.put("authentication-service", configurer.getResolvedProps().getProperty(ComponentNames.AUTHENTICATION_SERVICE));
+        final Properties props = configurer.getResolvedProps();
+        info.put("authentication-service", props.getProperty(ComponentNames.AUTHENTICATION_SERVICE));
         info.put("authentication-service.switch-aai.link",
-                configurer.getResolvedProps().getProperty(ComponentNames.AUTHENTICATION_SERVICE + "." + Constants.SWITCH_AAI_LINK));
+                props.getProperty(ComponentNames.AUTHENTICATION_SERVICE + "." + Constants.SWITCH_AAI_LINK));
         info.put("authentication-service.switch-aai.label",
-                configurer.getResolvedProps().getProperty(ComponentNames.AUTHENTICATION_SERVICE + "." + Constants.SWITCH_AAI_LABEL));
-        info.put("openbis.support.email", configurer.getResolvedProps().getProperty(ComponentNames.OPENBIS_SUPPORT_EMAIL));
+                props.getProperty(ComponentNames.AUTHENTICATION_SERVICE + "." + Constants.SWITCH_AAI_LABEL));
+        info.put("openbis.support.email", props.getProperty(ComponentNames.OPENBIS_SUPPORT_EMAIL));
+
+        // Put all properties of the format "server-public-information.x", where x is the rest of the string
+        for (final Map.Entry<?, ?> propEntry : props.entrySet())
+        {
+            final String key = (String) propEntry.getKey();
+            if (key.startsWith(ComponentNames.SERVER_PUBLIC_INFORMATION))
+            {
+                info.put(key, propEntry.getValue().toString());
+            }
+        }
+
         return new GetServerPublicInformationOperationResult(info);
     }
 
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
index 6a8734a71639426b2b38e63019e7cdc6005718b1..e58f1ab03ca15bf1f737e55359eabe7fd9175be5 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
@@ -54,4 +54,6 @@ public final class ComponentNames
     public static final String JYTHON_EVALUATOR_POOL = "jython-evaluator-pool";
 
     public static final String OPENBIS_SUPPORT_EMAIL = "openbis.support.email";
+
+    public static final String SERVER_PUBLIC_INFORMATION = "server-public-information.";
 }
diff --git a/server-application-server/source/java/service.properties b/server-application-server/source/java/service.properties
index b3b0ceced05362821b6b6e74d82cf627e88f09df..d7a8947b7b5b6353e71176bb2bef0a26804266c4 100644
--- a/server-application-server/source/java/service.properties
+++ b/server-application-server/source/java/service.properties
@@ -306,3 +306,10 @@ api.v3.transaction.participant.afs-server.url = http://localhost:8085/data-store
 
 # A timeout in seconds for the afs server operations. Default: 3600
 # api.v3.transaction.participant.afs-server.timeout =
+
+
+#
+# Server public information (returned by the V3 API's getServerPublicInformation() method)
+#
+
+server-public-information.afs-server.url = http://localhost:8085/data-store-server
\ No newline at end of file
diff --git a/ui-admin/index.html b/ui-admin/index.html
index 55c8686f7de64fe84be81eafe69e99ac7faf352e..337d6d57109b8c3e3cce8457955ac811be55a3bb 100644
--- a/ui-admin/index.html
+++ b/ui-admin/index.html
@@ -42,7 +42,6 @@
     <script src="/openbis/resources/api/data-store-server/api/jszip.min.js"></script>
     <script src="https://cdn.jsdelivr.net/npm/streamsaver@2"></script>
     <script src="/openbis/resources/api/v3/require.js"></script>
-    <script src="/openbis/resources/api/data-store-server/api/server-data-store-facade.js"></script>
   </head>
   <body>
     <div id="app"></div>
diff --git a/ui-admin/src/js/components/database/data-browser/DataBrowser.jsx b/ui-admin/src/js/components/database/data-browser/DataBrowser.jsx
index da902e418871cb2f66b1002b1eae61d400e102c3..06c3328b4b048a2d5b1cc2050740defec1cffe1a 100644
--- a/ui-admin/src/js/components/database/data-browser/DataBrowser.jsx
+++ b/ui-admin/src/js/components/database/data-browser/DataBrowser.jsx
@@ -13,8 +13,6 @@ import DataBrowserController from '@src/js/components/database/data-browser/Data
 import messages from '@src/js/common/messages.js'
 import InfoBar from '@src/js/components/database/data-browser/InfoBar.jsx'
 
-const HTTP_SERVER_URI = '/data-store-server'
-
 const styles = theme => ({
   columnFlexContainer: {
     flexDirection: 'column',
@@ -251,11 +249,6 @@ class DataBrowser extends React.Component {
 
     this.controller = controller || new DataBrowserController(id)
     this.controller.attach(this)
-    this.datastoreServer = new DataStoreServer(
-      'http://localhost:8085',
-      HTTP_SERVER_URI
-    )
-    this.controller.setSessionToken(sessionToken)
 
     this.state = {
       viewType: props.viewType,
@@ -461,7 +454,6 @@ class DataBrowser extends React.Component {
           onDownload={this.handleDownload}
           showInfo={showInfo}
           multiselectedFiles={multiselectedFiles}
-          datastoreServer={this.datastoreServer}
           sessionToken={sessionToken}
           owner={id}
           path={path}
diff --git a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js
index 995aa16ed8dfb2347db2a0d339d96bd0aa62b7f5..ba17aa0134c1f5489e8b9940239839978894d392 100644
--- a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js
+++ b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js
@@ -16,6 +16,7 @@
 
 import ComponentController from '@src/js/components/common/ComponentController.js'
 import autoBind from 'auto-bind'
+import openbis from '@src/js/services/openbis.js'
 
 const CHUNK_SIZE = 1024 * 1024 // 1MiB
 
@@ -31,50 +32,30 @@ export default class DataBrowserController extends ComponentController {
     this.fileNames = []
   }
 
-  setSessionToken(sessionToken) {
-    this.component.datastoreServer.useSession(sessionToken)
-  }
-
-  free() {
-    return new Promise((resolve, reject) => {
-      this.component.datastoreServer.free(this.owner, this.path)
-        .then((data) => {
-          if (!data.error) {
-            resolve(data);
-          } else {
-            reject(data.error)
-          }
-        })
-        .catch((error) => {
-          if (error.message.includes('NoSuchFileException')) {
-            resolve([])
-          } else {
-            reject(error)
-          }
-        })
-    })
+  async free() {
+    try {
+      return await openbis.free(this.owner, this.path);
+    } catch (error) {
+      if (error.message.includes('NoSuchFileException')) {
+        return []
+      } else {
+        throw error
+      }
+    }
   }
 
   async listFiles(path) {
     // Use this.path if path is not specified
     const pathToList = path ? path : this.path
-    return new Promise((resolve, reject) => {
-      this.component.datastoreServer.list(this.owner, pathToList, false)
-        .then((data) => {
-          if (!data.error) {
-            resolve(data)
-          } else {
-            reject(data.error)
-          }
-        })
-        .catch((error) => {
-          if (error.message.includes('NoSuchFileException')) {
-            resolve([])
-          } else {
-            reject(error)
-          }
-        })
-    })
+    try {
+      return await openbis.list(this.owner, pathToList, false)
+    } catch (error) {
+      if (error.message.includes('NoSuchFileException')) {
+        return []
+      } else {
+        throw error
+      }
+    }
   }
 
   async load() {
@@ -90,14 +71,14 @@ export default class DataBrowserController extends ComponentController {
   }
 
   async createNewFolder(name) {
-    await this.component.datastoreServer.create(this.owner, this.path + name, true)
+    await openbis.create(this.owner, this.path + name, true)
     if (this.gridController) {
       await this.gridController.load()
     }
   }
 
   async rename(oldName, newName) {
-    await this.component.datastoreServer.move(this.owner, this.path + oldName, this.owner, this.path + newName)
+    await openbis.move(this.owner, this.path + oldName, this.owner, this.path + newName)
     if (this.gridController) {
       await this.gridController.load()
     }
@@ -114,7 +95,7 @@ export default class DataBrowserController extends ComponentController {
   }
 
   async _delete(file) {
-    await this.component.datastoreServer.delete(this.owner, file.path)
+    await openbis.delete(this.owner, file.path)
   }
 
   async copy(files, newLocation) {
@@ -130,7 +111,7 @@ export default class DataBrowserController extends ComponentController {
   async _copy(file, newLocation){
     if (!this.isSubdirectory(file.path, newLocation)) {
       const cleanNewLocation = this._removeLeadingSlash(newLocation) + file.name
-      await this.component.datastoreServer.copy(this.owner, file.path, this.owner, cleanNewLocation)
+      await openbis.copy(this.owner, file.path, this.owner, cleanNewLocation)
     }
   }
 
@@ -147,7 +128,7 @@ export default class DataBrowserController extends ComponentController {
   async _move(file, newLocation){
     if (!this.isSubdirectory(file.path, newLocation)) {
       const cleanNewLocation = this._removeLeadingSlash(newLocation) + file.name
-      await this.component.datastoreServer.move(this.owner, file.path, this.owner, cleanNewLocation)
+      await openbis.move(this.owner, file.path, this.owner, cleanNewLocation)
     }
   }
 
@@ -220,7 +201,7 @@ export default class DataBrowserController extends ComponentController {
   }
 
   async _uploadChunk(source, offset, data) {
-    return await this.component.datastoreServer.write(this.owner, source, offset, data)
+    return await openbis.write(this.owner, source, offset, data)
   }
 
   async _arrayBufferToBase64(buffer) {
@@ -276,7 +257,7 @@ export default class DataBrowserController extends ComponentController {
 
   async _download(file, offset) {
     const limit = Math.min(CHUNK_SIZE, file.size - offset)
-    return await this.component.datastoreServer.read(this.owner, file.path, offset, limit)
+    return await openbis.read(this.owner, file.path, offset, limit)
   }
 
   _removeLeadingSlash(path) {
diff --git a/ui-admin/src/js/components/database/data-browser/LeftToolbar.jsx b/ui-admin/src/js/components/database/data-browser/LeftToolbar.jsx
index ab8dacbbe7415430f5b2bc7f93134fc11026b9ef..539619e3e05db4e744d014d93e24cffeb35d402b 100644
--- a/ui-admin/src/js/components/database/data-browser/LeftToolbar.jsx
+++ b/ui-admin/src/js/components/database/data-browser/LeftToolbar.jsx
@@ -216,7 +216,6 @@ class LeftToolbar extends React.Component {
       classes,
       buttonSize,
       multiselectedFiles,
-      datastoreServer,
       sessionToken,
       owner,
       path,
@@ -356,7 +355,6 @@ class LeftToolbar extends React.Component {
           open={!!locationDialogMode}
           title={locationDialogMode === moveLocationMode ? messages.get(messages.MOVE) : messages.get(messages.COPY)}
           content={messages.get(messages.FILE_OR_FILES, multiselectedFiles.size)}
-          datastoreServer={datastoreServer}
           sessionToken={sessionToken}
           owner={owner}
           path={path}
diff --git a/ui-admin/src/js/components/database/data-browser/LocationDialog.jsx b/ui-admin/src/js/components/database/data-browser/LocationDialog.jsx
index a4332823449188aec55c2f2fe1aad3570a212c35..64a4f6997eb81b5af8c7313347af178259ee6a1e 100644
--- a/ui-admin/src/js/components/database/data-browser/LocationDialog.jsx
+++ b/ui-admin/src/js/components/database/data-browser/LocationDialog.jsx
@@ -45,12 +45,10 @@ class LocationDialog extends React.Component {
     super(props)
     autoBind(this)
 
-    const { path, sessionToken, datastoreServer, owner } = this.props
+    const { path, sessionToken, owner } = this.props
 
     this.controller = new DataBrowserController(owner)
     this.controller.attach(this)
-    this.datastoreServer = datastoreServer
-    this.controller.setSessionToken(sessionToken)
 
     this.state = {
       path,
diff --git a/ui-admin/src/js/components/database/data-browser/Toolbar.jsx b/ui-admin/src/js/components/database/data-browser/Toolbar.jsx
index bdd7be6d9089797dde61e7399bcdf25b38b9f1aa..88bd9f8e4cffb43393125448a7c6c614f21ff53b 100644
--- a/ui-admin/src/js/components/database/data-browser/Toolbar.jsx
+++ b/ui-admin/src/js/components/database/data-browser/Toolbar.jsx
@@ -52,7 +52,6 @@ class Toolbar extends React.Component {
       showInfo,
       onShowInfoChange,
       multiselectedFiles,
-      datastoreServer,
       sessionToken,
       owner,
       path,
@@ -63,7 +62,6 @@ class Toolbar extends React.Component {
         <LeftToolbar
           buttonSize={buttonSize}
           multiselectedFiles={multiselectedFiles}
-          datastoreServer={datastoreServer}
           sessionToken={sessionToken}
           owner={owner}
           path={path}
diff --git a/ui-admin/src/js/services/openbis/api.js b/ui-admin/src/js/services/openbis/api.js
index e9061203f01497cdf0da408e629f1c6c1868ebcc..d79fc89e1d378fb298b26038aee61949f4ffe308 100644
--- a/ui-admin/src/js/services/openbis/api.js
+++ b/ui-admin/src/js/services/openbis/api.js
@@ -13,9 +13,21 @@ class Facade {
       requirejs(
         ['openbis', 'util/Json'],
         (openbis, stjs) => {
-          _this.v3 = new openbis()
-          _this.stjs = stjs
-          resolve()
+          new openbis().getServerPublicInformation()
+            .then(result => {
+              const afsServerUrlKey = 'server-public-information.afs-server.url'
+              const afsServerUrl = result[afsServerUrlKey]
+              if (afsServerUrl) {
+                _this.v3 = new openbis(null, afsServerUrl)
+                _this.stjs = stjs
+                resolve()
+              } else {
+                reject(new Error(afsServerUrlKey + ' is not set.'))
+              }
+            },
+            error => {
+              reject(error)
+            })
         },
         error => {
           reject(error)
@@ -248,6 +260,38 @@ class Facade {
     return this.promise(this.stjs.fromJson(jsonType, jsonObject));
   }
 
+  list(owner, source, recursively) {
+    return this.promise(this.v3.getAfsServerFacade().list(owner, source, recursively))
+  }
+
+  read(owner, source, offset, limit) {
+    return this.promise(this.v3.getAfsServerFacade().read(owner, source, offset, limit))
+  }
+
+  write(owner, source, offset, data) {
+    return this.promise(this.v3.getAfsServerFacade().write(owner, source, offset, data))
+  }
+
+  delete(owner, source) {
+    return this.promise(this.v3.getAfsServerFacade().delete(owner, source))
+  }
+
+  copy(sourceOwner, source, targetOwner, target) {
+    return this.promise(this.v3.getAfsServerFacade().copy(sourceOwner, source, targetOwner, target))
+  }
+
+  move(sourceOwner, source, targetOwner, target) {
+    return this.promise(this.v3.getAfsServerFacade().move(sourceOwner, source, targetOwner, target))
+  }
+
+  create(owner, source, directory) {
+    return this.promise(this.v3.getAfsServerFacade().create(owner, source, directory))
+  }
+
+  free(owner, source) {
+    return this.promise(this.v3.getAfsServerFacade().free(owner, source))
+  }
+
   async executeService(id, options) {
     const scheduleResult = await this.executeOperations(
       [new dto.ExecuteCustomASServiceOperation(id, options)],