diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java
index fb7010325f5165428e8a09c3ddb4b9e19fc8bcf8..57b472396c243f369491faa3962dfe9c39b69831 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java
@@ -1,6 +1,7 @@
 package at.tuwien.entities.auth;
 
 import lombok.*;
+import org.hibernate.annotations.GenericGenerator;
 
 import javax.persistence.*;
 
@@ -16,7 +17,9 @@ public class Realm {
 
     @Id
     @EqualsAndHashCode.Include
-    @Column(nullable = false)
+    @GeneratedValue(generator = "realm-uuid")
+    @GenericGenerator(name = "realm-uuid", strategy = "org.hibernate.id.UUIDGenerator")
+    @Column(name = "ID", nullable = false, columnDefinition = "VARCHAR(36)")
     private String id;
 
     @Column(nullable = false)
@@ -25,7 +28,4 @@ public class Realm {
     @Column(nullable = false)
     private String name;
 
-    @Column(nullable = false)
-    private String sslRequired;
-
 }
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java
new file mode 100644
index 0000000000000000000000000000000000000000..a189fb25d023b90a606762c3e19f97a78476fa8a
--- /dev/null
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java
@@ -0,0 +1,51 @@
+package at.tuwien.entities.user;
+
+import lombok.*;
+import org.hibernate.annotations.GenericGenerator;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+
+@Data
+@Entity
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+@EntityListeners(AuditingEntityListener.class)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@Table(name = "credential")
+public class Credential {
+
+    @Id
+    @EqualsAndHashCode.Include
+    @GeneratedValue(generator = "credential-uuid")
+    @GenericGenerator(name = "credential-uuid", strategy = "org.hibernate.id.UUIDGenerator")
+    @Column(name = "ID", nullable = false, columnDefinition = "VARCHAR(36)")
+    private String id;
+
+    @Column(nullable = false)
+    private String type;
+
+    @Column(name = "user_id", nullable = false)
+    private String userId;
+
+    @Column(nullable = false)
+    private Long createdDate;
+
+    @Column(nullable = false, columnDefinition = "LONGTEXT")
+    private String secretData;
+
+    @Column(nullable = false, columnDefinition = "LONGTEXT")
+    private String credentialData;
+
+    @Column(nullable = false)
+    private Integer priority;
+
+    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+    @JoinColumns({
+            @JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
+    })
+    private User user;
+
+}
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
index 29f7222a7159524f446b764964ba361d3fea32d7..af7c01362072239002a8639480010b90b403932a 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
@@ -4,6 +4,7 @@ import at.tuwien.entities.container.Container;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.identifier.Identifier;
 import lombok.*;
+import org.hibernate.annotations.GenericGenerator;
 import org.hibernate.annotations.Immutable;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
@@ -16,7 +17,6 @@ import java.util.List;
 @AllArgsConstructor
 @NoArgsConstructor
 @ToString
-@Immutable
 @EntityListeners(AuditingEntityListener.class)
 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
 @Table(name = "user_entity")
@@ -24,7 +24,9 @@ public class User {
 
     @Id
     @EqualsAndHashCode.Include
-    @Column(nullable = false)
+    @GeneratedValue(generator = "user-uuid")
+    @GenericGenerator(name = "user-uuid", strategy = "org.hibernate.id.UUIDGenerator")
+    @Column(name = "ID", nullable = false, columnDefinition = "VARCHAR(36)")
     private String id;
 
     @Column(unique = true, nullable = false)
@@ -45,11 +47,21 @@ public class User {
     @Column(nullable = false)
     private Boolean emailVerified;
 
+    @Column(nullable = false)
+    private Boolean enabled;
+
+    @Column
+    private Long createdTimestamp;
+
     @Transient
     @ToString.Exclude
     @Column(nullable = false)
     private String databasePassword;
 
+    @Column(nullable = false)
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+    private List<Credential> credentials;
+
     @Transient
     @ToString.Exclude
     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "owner")
diff --git a/fda-ui/api/analyse/index.js b/fda-ui/api/analyse/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..66ad332a56dfc7b58104f77c5c8fdd206e4f25a7
--- /dev/null
+++ b/fda-ui/api/analyse/index.js
@@ -0,0 +1,12 @@
+const axios = require('axios/dist/browser/axios.cjs')
+
+export function determineDataTypes (token, filepath) {
+  const payload = {
+    filepath
+  }
+  return axios.post('/api/analyse/determinedt', payload, {
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  })
+}
diff --git a/fda-ui/api/container/index.js b/fda-ui/api/container/index.js
index f5b6e59f80e395734784f08923d85adb48727ed4..594657500b1fa953ce3592333fe1a01940da9f47 100644
--- a/fda-ui/api/container/index.js
+++ b/fda-ui/api/container/index.js
@@ -4,6 +4,10 @@ export function listContainers (limit) {
   return axios.get(`/api/container?limit=${limit}`)
 }
 
+export function findContainer (containerId) {
+  return axios.get(`/api/container${containerId}`)
+}
+
 export function createContainer (token, payload) {
   return axios.post('/api/container/', payload, {
     headers: {
diff --git a/fda-ui/api/database/index.js b/fda-ui/api/database/index.js
index 986311fec70fbd6e6104b87451a46cd4c594904e..dee8179bf5dbfefba9426f060ebef5664caa4e49 100644
--- a/fda-ui/api/database/index.js
+++ b/fda-ui/api/database/index.js
@@ -23,6 +23,17 @@ export function modifyVisibility (token, containerId, databaseId, isPublic) {
   })
 }
 
+export function modifyOwnership (token, containerId, databaseId, username) {
+  const payload = {
+    username
+  }
+  return axios.put(`/api/container/${containerId}/database/${databaseId}/transfer`, payload, {
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  })
+}
+
 export function findDatabase (token, containerId, databaseId) {
   return axios.get(`/api/container/${containerId}/database/${databaseId}`, {
     headers: {
diff --git a/fda-ui/api/table/index.js b/fda-ui/api/table/index.js
index d0f5aa0b6fbb21b1610d13ef6ee45e5f7daa0109..097d003972837b4d640125704831315256360bfb 100644
--- a/fda-ui/api/table/index.js
+++ b/fda-ui/api/table/index.js
@@ -15,3 +15,11 @@ export function createTable (token, containerId, databaseId, payload) {
     }
   })
 }
+
+export function dataImport (token, containerId, databaseId, tableId, payload) {
+  return axios.post(`/api/container/${containerId}/database/${databaseId}/table/${tableId}/data/import`, payload, {
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  })
+}
diff --git a/fda-ui/api/user/index.js b/fda-ui/api/user/index.js
index 8f3edc2f2c93cd476e8d5210e6c6f57fef7dfb08..670535c449675ff6a20af05e5bc89fedb65e6bac 100644
--- a/fda-ui/api/user/index.js
+++ b/fda-ui/api/user/index.js
@@ -51,10 +51,16 @@ export function tokenToUser (token) {
 
 export function tokenToExp (token) {
   const data = jwt_decode(token)
+  if (!data) {
+    return new Date()
+  }
   return new Date(data.exp * 1000)
 }
 
 export function tokenToRoles (token) {
   const data = jwt_decode(token)
-  return data.realm_access.roles
+  if (!data) {
+    return []
+  }
+  return data.realm_access.roles || []
 }
diff --git a/fda-ui/components/DatabaseList.vue b/fda-ui/components/DatabaseList.vue
index 572370fd26be5f68a538cece4bf8db087a2fbace..81e96814351acfe3f16a99e3a0a48cc0e84158d7 100644
--- a/fda-ui/components/DatabaseList.vue
+++ b/fda-ui/components/DatabaseList.vue
@@ -98,20 +98,20 @@ export default {
   },
   methods: {
     formatOwner (container) {
-      if (!('database' in container)) {
+      if (!('database' in container) || !container.database) {
         return formatUser(container.creator)
       }
       return formatUser(container.database?.owner)
     },
     formatCreators (container) {
       const creators = formatCreators(container)
-      return creators || this.formatCreator(container.creator)
+      return creators || this.formatUser(container.creator)
     },
     canInit (container) {
       if (!this.user) {
         return false
       }
-      if (container.creator.sub !== this.user.sub) {
+      if (container.creator.username !== this.user.username) {
         return false
       }
       return !container.database
diff --git a/fda-ui/config.js b/fda-ui/config.js
index b2ea4ef41e7c38bb63c62df110c71fbfd78cb82d..e2bc44ee35ecae1e00629b55538f13239466bb1e 100644
--- a/fda-ui/config.js
+++ b/fda-ui/config.js
@@ -1,6 +1,6 @@
 const config = {}
 
-config.api = process.env.API || 'http://localhost:9095'
+config.api = process.env.API || 'https://gateway-service:9095'
 config.search = process.env.SEARCH || 'http://localhost:9200'
 config.sandbox = process.env.SANDBOX || false
 config.title = process.env.TITLE || 'Database Repository'
diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue
index 2f68a1ade6cbbe5baedcdf92565ed28883df4a3f..064a8436d0588c230a041f253fc5c40c68d6a707 100644
--- a/fda-ui/layouts/default.vue
+++ b/fda-ui/layouts/default.vue
@@ -252,7 +252,7 @@ export default {
       this.$router.push({ path: '/login', query: redirect ? { redirect: this.$router.currentRoute.path } : {} })
     },
     logout (message) {
-      if (message) {
+      if (typeof message === 'string') {
         this.$toast.warning(message)
       }
       this.$store.commit('SET_TOKEN', null)
diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js
index 1f4565dffb49e5e3e18588df6561bf4d53e4a121..f84af6fa4507edf8b084a2ffc3ed978bea1797c7 100644
--- a/fda-ui/nuxt.config.js
+++ b/fda-ui/nuxt.config.js
@@ -84,7 +84,7 @@ export default {
   proxy: {
     '/api': api,
     '/pid': {
-      target: process.env.API + '/api' || 'https://localhost:9095/api',
+      target: api + '/api',
       changeOrigin: true,
       pathRewrite: {
         '^/pid': '/pid'
diff --git a/fda-ui/package.json b/fda-ui/package.json
index ff23e62ae94a9fff5f6ad2d24cc4092fa5e861d5..2548fe968b7e116e56a99218d5e85e775b56055c 100644
--- a/fda-ui/package.json
+++ b/fda-ui/package.json
@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "private": true,
   "scripts": {
-    "dev": "nuxt --port 3001",
+    "dev": "NODE_OPTIONS=--use-openssl-ca NODE_EXTRA_CA_CERTS=./root.crt nuxt --port 3001",
     "docker": "nuxt > /dev/null",
     "build": "nuxt build",
     "start": "nuxt start",
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/settings.vue b/fda-ui/pages/container/_container_id/database/_database_id/settings.vue
index 888ba4f681117fd66f97a8df17748ec5b3998450..2c4fc09eeb7e447015d0822ee0b6ecef9e406fb4 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/settings.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/settings.vue
@@ -64,6 +64,8 @@
                   id="owner"
                   v-model="modifyOwner.username"
                   :items="users"
+                  item-text="username"
+                  item-value="username"
                   label="Owner"
                   name="owner" />
               </v-col>
@@ -72,8 +74,8 @@
               small
               color="warning"
               class="black--text"
-              @click="updateDatabaseVisibility">
-              Modify Visibility
+              @click="updateDatabaseOwner">
+              Modify Ownership
             </v-btn>
           </v-card-text>
         </v-card>
@@ -91,7 +93,7 @@
 <script>
 import DBToolbar from '@/components/DBToolbar'
 import EditAccess from '@/components/dialogs/EditAccess'
-import { modifyVisibility } from '@/api/database'
+import { modifyVisibility, modifyOwnership } from '@/api/database'
 
 export default {
   components: {
@@ -186,6 +188,7 @@ export default {
         return
       }
       this.modifyVisibility.is_public = this.database.is_public
+      this.modifyOwner.username = this.database.owner.username
     }
   },
   mounted () {
@@ -194,6 +197,7 @@ export default {
       return
     }
     this.modifyVisibility.is_public = this.database.is_public
+    this.modifyOwner.username = this.database.owner.username
   },
   methods: {
     closeDialog (event) {
@@ -215,6 +219,17 @@ export default {
       }
       this.loading = false
     },
+    async updateDatabaseOwner () {
+      try {
+        this.loading = true
+        await modifyOwnership(this.token, this.$route.params.container_id, this.$route.params.database_id, this.modifyOwner.username)
+        this.$toast.success('Successfully updated the database owner')
+      } catch (error) {
+        console.error('Failed to update database owner', error)
+        this.$toast.error('Failed to update database owner')
+      }
+      this.loading = false
+    },
     giveAccess () {
       this.username = null
       this.editAccessDialog = true
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
index 634c92fbed91ef8cb7434461b7c6ddaf6c8616ee..f94d97345ffaf8d03f5e829fb6a0f97cc32b1c5c 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
@@ -187,7 +187,9 @@
 <script>
 import TableSchema from '@/components/TableSchema'
 import { notEmpty, isNonNegativeInteger, isResearcher } from '@/utils'
-import { listTables } from '@/api/table'
+import { findContainer } from '@/api/container'
+import { listTables, createTable, dataImport } from '@/api/table'
+import { determineDataTypes } from '@/api/analyse'
 
 export default {
   name: 'TableFromCSV',
@@ -317,7 +319,6 @@ export default {
         this.file = res.data
       } catch (err) {
         console.error('Failed to upload .csv data', err)
-        console.debug('failed to upload .csv data, does the .csv contain a header line?')
         this.$toast.error('Could not upload data')
       }
       this.loading = false
@@ -325,8 +326,7 @@ export default {
     async analyse () {
       this.loading = true
       try {
-        const payload = { filepath: `/tmp/${this.file.filename}` }
-        const res = await this.$axios.post('/api/analyse/determinedt', payload, this.config)
+        const res = await determineDataTypes(this.token, `/tmp/${this.file.filename}`)
         const { columns } = res.data
         console.log('data analyse result', columns)
         this.tableCreate.columns = Object.entries(columns)
@@ -334,12 +334,8 @@ export default {
             return {
               name: key,
               type: val,
-              check_expression: null,
-              foreign_key: null,
-              references: null,
               null_allowed: true,
               primary_key: false,
-              unique: false,
               enum_values: []
             }
           })
@@ -382,11 +378,10 @@ export default {
       column.unique = true
     },
     async loadDateFormats () {
-      const getUrl = `/api/container/${this.$route.params.container_id}`
       let getResult
       try {
         this.loading = true
-        getResult = await this.$axios.get(getUrl, this.config)
+        getResult = await findContainer(this.$route.params.container_id)
         this.dateFormats = getResult.data.image.date_formats
         console.debug('retrieve image date formats', this.dateFormats)
         this.loading = false
@@ -415,11 +410,10 @@ export default {
       // bail out if there is a problem with one of the columns
       if (!validColumns.every(Boolean)) { return }
 
-      const createUrl = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table`
       let createResult
       try {
         this.loading = true
-        createResult = await this.$axios.post(createUrl, this.tableCreate, this.config)
+        createResult = await createTable(this.token, this.$route.params.container_id, this.$route.params.database_id, this.tableCreate)
         this.newTableId = createResult.data.id
         console.debug('created table', createResult.data)
       } catch (err) {
@@ -433,10 +427,9 @@ export default {
         console.error('create table failed', err)
         return
       }
-      const insertUrl = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${createResult.data.id}/data/import`
       let insertResult
       try {
-        insertResult = await this.$axios.post(insertUrl, this.tableImport, this.config)
+        insertResult = await dataImport(this.token, this.$route.params.container_id, this.$route.params.database_id, createResult.data.id, this.tableImport)
         console.debug('inserted table', insertResult.data)
       } catch (err) {
         this.loading = false
diff --git a/fda-ui/utils/index.js b/fda-ui/utils/index.js
index 3772dbb7a808a9dcf52ab2a0c0f2b12a6b18f93d..09b2314944314db688b7505166670251a709b461 100644
--- a/fda-ui/utils/index.js
+++ b/fda-ui/utils/index.js
@@ -54,10 +54,10 @@ function formatUser (user) {
   if (!user) {
     return null
   }
-  if (!('firstname' in user) || !('lastname' in user) || user.firstname === null || user.lastname === null) {
+  if (!('given_name' in user) || !('family_name' in user) || user.given_name === null || user.family_name === null) {
     return user?.username
   }
-  return user.firstname + ' ' + user.lastname
+  return user.given_name + ' ' + user.family_name
 }
 
 function formatDateUTC (str) {
diff --git a/fda-user-service/pom.xml b/fda-user-service/pom.xml
index 989dafab4ceaf1fb5bc6dbcdf3027cb8821e8b3d..8b3b45fbf1906a8e8d0261b470e5449dbed44645 100644
--- a/fda-user-service/pom.xml
+++ b/fda-user-service/pom.xml
@@ -51,6 +51,7 @@
         <hibernate-c3po.version>5.6.3.Final</hibernate-c3po.version>
         <maven-report.version>3.0.0</maven-report.version>
         <jwt.version>4.3.0</jwt.version>
+        <keycloak.version>21.0.2</keycloak.version>
     </properties>
 
     <dependencies>
@@ -97,6 +98,11 @@
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
         <!-- Authentication -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-common</artifactId>
+            <version>${keycloak.version}</version>
+        </dependency>
         <dependency>
             <groupId>com.auth0</groupId>
             <artifactId>java-jwt</artifactId>
diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
index 485dcccce4861baed02335cc8de4e05248631ea2..0d5148892f22a65c92272cd841f559879203d929 100644
--- a/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
+++ b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
@@ -2,9 +2,13 @@ package at.tuwien.endpoint;
 
 import at.tuwien.api.auth.SignupRequestDto;
 import at.tuwien.api.user.UserBriefDto;
+import at.tuwien.entities.auth.Realm;
+import at.tuwien.exception.RealmNotFoundException;
 import at.tuwien.exception.RemoteUnavailableException;
+import at.tuwien.exception.UserAlreadyExistsException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.mapper.UserMapper;
+import at.tuwien.service.RealmService;
 import at.tuwien.service.UserService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -28,11 +32,13 @@ public class UserEndpoint {
 
     private final UserMapper userMapper;
     private final UserService userService;
+    private final RealmService realmService;
 
     @Autowired
-    public UserEndpoint(UserMapper userMapper, UserService userService) {
+    public UserEndpoint(UserMapper userMapper, UserService userService, RealmService realmService) {
         this.userMapper = userMapper;
         this.userService = userService;
+        this.realmService = realmService;
     }
 
     @GetMapping
@@ -54,9 +60,11 @@ public class UserEndpoint {
     @Timed(value = "user.create", description = "Time needed to create a user in the metadata database")
     @Operation(summary = "Create a user")
     public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody SignupRequestDto data)
-            throws UserNotFoundException, RemoteUnavailableException {
+            throws UserNotFoundException, RemoteUnavailableException, RealmNotFoundException,
+            UserAlreadyExistsException {
         log.debug("endpoint create a user, data={}", data);
-        final UserBriefDto dto = userMapper.userToUserBriefDto(userService.create(data));
+        final Realm realm = realmService.find("dbrepo");
+        final UserBriefDto dto = userMapper.userToUserBriefDto(userService.create(data, realm));
         log.trace("create user resulted in dto {}", dto);
         return ResponseEntity.status(HttpStatus.CREATED)
                 .body(dto);
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..1750cfb525c2947f8f13837b5e89ed7ddc46f8fd
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class RealmNotFoundException extends Exception {
+
+    public RealmNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public RealmNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public RealmNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/UserAlreadyExistsException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/UserAlreadyExistsException.java
new file mode 100644
index 0000000000000000000000000000000000000000..bca8199ae01a314e400ba065d367efe84dc6e972
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/UserAlreadyExistsException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.CONFLICT)
+public class UserAlreadyExistsException extends Exception {
+
+    public UserAlreadyExistsException(String message) {
+        super(message);
+    }
+
+    public UserAlreadyExistsException(String message, Throwable thr) {
+        super(message, thr);
+    }
+
+    public UserAlreadyExistsException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
index 308cda436322a23d4509a10e7630dea8c0775065..1313e12b3b702a085222b5b77b52fdd228fb034f 100644
--- a/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
+++ b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
@@ -1,7 +1,5 @@
 package at.tuwien.mapper;
 
-import at.tuwien.api.auth.CreateUserDto;
-import at.tuwien.api.auth.CredentialDto;
 import at.tuwien.api.auth.SignupRequestDto;
 import at.tuwien.api.user.GrantedAuthorityDto;
 import at.tuwien.api.user.UserBriefDto;
@@ -12,7 +10,6 @@ import org.mapstruct.Mapper;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 
-import java.util.List;
 
 @Mapper(componentModel = "spring")
 public interface UserMapper {
@@ -25,18 +22,7 @@ public interface UserMapper {
 
     UserBriefDto userToUserBriefDto(User data);
 
-    default CreateUserDto signupRequestDtoToCreateUserDto(SignupRequestDto data) {
-        return CreateUserDto.builder()
-                .username(data.getUsername())
-                .email(data.getEmail())
-                .enabled(true)
-                .credentials(List.of(CredentialDto.builder()
-                        .temporary(false)
-                        .type("password")
-                        .value(data.getPassword())
-                        .build()))
-                .build();
-    }
+    User signupRequestDtoToUser(SignupRequestDto data);
 
     default GrantedAuthority grantedAuthorityDtoToGrantedAuthority(GrantedAuthorityDto data) {
         final GrantedAuthority authority = new SimpleGrantedAuthority(data.getAuthority());
diff --git a/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/CredentialRepository.java b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/CredentialRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb43a5af74d5863735c9f532351a4f40bb6f35aa
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/CredentialRepository.java
@@ -0,0 +1,10 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.user.Credential;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CredentialRepository extends JpaRepository<Credential, String> {
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..db0443c0a78c3c8c2d653edf15901c9a3f2e4686
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java
@@ -0,0 +1,14 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.auth.Realm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface RealmRepository extends JpaRepository<Realm, String> {
+
+    Optional<Realm> findByName(String name);
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java b/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java
new file mode 100644
index 0000000000000000000000000000000000000000..768b437a715f78830e3b1a8182cbc90a4a514614
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java
@@ -0,0 +1,16 @@
+package at.tuwien.service;
+
+import at.tuwien.entities.auth.Realm;
+import at.tuwien.exception.RealmNotFoundException;
+
+public interface RealmService {
+
+    /**
+     * Finds a realm by name.
+     *
+     * @param name The realm name.
+     * @return The realm, if successful.
+     * @throws RealmNotFoundException The realm could not be found.
+     */
+    Realm find(String name) throws RealmNotFoundException;
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
index a44429ae15846283fe9a6efca7a7cb5a6856c342..33addb5732aac74028b7b5e9ce480bed32eda19a 100644
--- a/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
@@ -1,8 +1,10 @@
 package at.tuwien.service;
 
 import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.entities.auth.Realm;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.RemoteUnavailableException;
+import at.tuwien.exception.UserAlreadyExistsException;
 import at.tuwien.exception.UserNotFoundException;
 
 import java.util.List;
@@ -25,22 +27,7 @@ public interface UserService {
      */
     User findByUsername(String username) throws UserNotFoundException;
 
-    /**
-     * Create a user in the authentication service.
-     *
-     * @param data The user data.
-     * @return The user, if successful.
-     * @throws RemoteUnavailableException
-     * @throws UserNotFoundException
-     */
-    User create(SignupRequestDto data) throws RemoteUnavailableException, UserNotFoundException;
+    User create(SignupRequestDto data, Realm realm) throws RemoteUnavailableException, UserNotFoundException, UserAlreadyExistsException;
 
-    /**
-     * Finds a user by id.
-     *
-     * @param id The id.
-     * @return The user.
-     * @throws UserNotFoundException The user was not found in the metadata database.
-     */
     User find(String id) throws UserNotFoundException;
 }
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java b/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c971add7fe6deed7968b00a7980e868dfad382c
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java
@@ -0,0 +1,35 @@
+package at.tuwien.service.impl;
+
+import at.tuwien.entities.auth.Realm;
+import at.tuwien.exception.RealmNotFoundException;
+import at.tuwien.repository.jpa.RealmRepository;
+import at.tuwien.service.RealmService;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Log4j2
+@Service
+public class RealmServiceImpl implements RealmService {
+
+    private final RealmRepository realmRepository;
+
+    @Autowired
+    public RealmServiceImpl(RealmRepository realmRepository) {
+        this.realmRepository = realmRepository;
+    }
+
+    @Override
+    public Realm find(String name) throws RealmNotFoundException {
+        final Optional<Realm> optional = realmRepository.findByName(name);
+        if (optional.isEmpty()) {
+            log.error("Failed to find realm with name '{}'", name);
+            throw new RealmNotFoundException("Failed to find realm");
+        }
+        final Realm realm = optional.get();
+        log.trace("found realm {}", realm);
+        return realm;
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
index ada4ce60f13e92c330d13a41be2d478e03b97eda..514460cf5642aaf48706b9fe06324e15dc96d186 100644
--- a/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
@@ -1,19 +1,29 @@
 package at.tuwien.service.impl;
 
-import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.auth.SignupRequestDto;
-import at.tuwien.api.auth.TokenDto;
+import at.tuwien.entities.auth.Realm;
+import at.tuwien.entities.user.Credential;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.RemoteUnavailableException;
+import at.tuwien.exception.UserAlreadyExistsException;
 import at.tuwien.exception.UserNotFoundException;
-import at.tuwien.gateway.GatewayServiceGateway;
 import at.tuwien.mapper.UserMapper;
+import at.tuwien.repository.jpa.CredentialRepository;
 import at.tuwien.repository.jpa.UserRepository;
 import at.tuwien.service.UserService;
 import lombok.extern.log4j.Log4j2;
+import org.keycloak.common.util.Base64;
+import org.keycloak.common.util.PaddingUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.time.Instant;
 import java.util.List;
 import java.util.Optional;
 
@@ -21,16 +31,23 @@ import java.util.Optional;
 @Service
 public class UserServiceImpl implements UserService {
 
+
+    private static final String ID = "pbkdf2-sha256";
+    private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
+    private static final int DEFAULT_ITERATIONS = 27500;
+    private static final Integer DERIVED_KEY_SIZE = 256;
+    private static final Integer MAX_PADDING_LENGTH = 14;
+
     private final UserMapper userMapper;
     private final UserRepository userRepository;
-    private final GatewayServiceGateway authenticationServiceGateway;
+    private final CredentialRepository credentialRepository;
 
     @Autowired
     public UserServiceImpl(UserMapper userMapper, UserRepository userRepository,
-                           GatewayServiceGateway authenticationServiceGateway) {
+                           CredentialRepository credentialRepository) {
         this.userMapper = userMapper;
         this.userRepository = userRepository;
-        this.authenticationServiceGateway = authenticationServiceGateway;
+        this.credentialRepository = credentialRepository;
     }
 
     @Override
@@ -49,17 +66,41 @@ public class UserServiceImpl implements UserService {
     }
 
     @Override
-    public User create(SignupRequestDto data) throws RemoteUnavailableException, UserNotFoundException {
-        final TokenDto tokenDto = authenticationServiceGateway.getToken();
-        final CreateUserDto userDto = userMapper.signupRequestDtoToCreateUserDto(data);
-        authenticationServiceGateway.createUser(tokenDto.getAccessToken(), userDto);
+    public User create(SignupRequestDto data, Realm realm) throws RemoteUnavailableException, UserNotFoundException,
+            UserAlreadyExistsException {
+        /* check */
         final Optional<User> optional = userRepository.findByUsername(data.getUsername());
-        if (optional.isEmpty()) {
-            /* should never occur */
-            throw new UserNotFoundException("User not found with username '" + data.getUsername() + "'");
+        if (optional.isPresent()) {
+            log.error("User with username {} already exists", data.getUsername());
+            throw new UserAlreadyExistsException("User with username " + data.getUsername() + " already exists");
         }
-        final User user = optional.get();
+        /* create secret */
+
+        /* save */
+        final User tmp = userMapper.signupRequestDtoToUser(data);
+        tmp.setEmailVerified(false);
+        tmp.setEnabled(true);
+        tmp.setRealmId(realm.getId());
+        tmp.setCreatedTimestamp(Instant.now().toEpochMilli());
+        final byte[] salt = getSalt();
+        final StringBuilder secretData = new StringBuilder("{\"value\":\"")
+                .append(encodedCredential(data.getPassword(), DEFAULT_ITERATIONS, salt, DERIVED_KEY_SIZE))
+                .append("\",\"salt\":\"")
+                .append(Base64.encodeBytes(salt))
+                .append("\",\"additionalParameters\":{}}");
+        final Credential entity = Credential.builder()
+                .createdDate(Instant.now().toEpochMilli())
+                .secretData(secretData.toString())
+                .type("password")
+                .priority(10)
+                .credentialData("{\"hashIterations\":" + DEFAULT_ITERATIONS + ",\"algorithm\":\"" + ID + "\",\"additionalParameters\":{}}")
+                .build();
+        final User user = userRepository.save(tmp);
+        entity.setUserId(user.getId());
+        final Credential credential = credentialRepository.save(entity);
+        user.setCredentials(List.of(credential));
         log.info("Created user with id {}", user.getId());
+        log.debug("created user {}", user);
         return user;
     }
 
@@ -73,4 +114,32 @@ public class UserServiceImpl implements UserService {
         return optional.get();
     }
 
+    private String encodedCredential(String rawPassword, int iterations, byte[] salt, int derivedKeySize) {
+        final String rawPasswordWithPadding = PaddingUtils.padding(rawPassword, MAX_PADDING_LENGTH);
+        log.trace("padding: {}", rawPasswordWithPadding);
+        final KeySpec spec = new PBEKeySpec(rawPasswordWithPadding.toCharArray(), salt, iterations, derivedKeySize);
+        try {
+            byte[] key = getSecretKeyFactory().generateSecret(spec).getEncoded();
+            return Base64.encodeBytes(key);
+        } catch (InvalidKeySpecException e) {
+            throw new RuntimeException("Credential could not be encoded", e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private byte[] getSalt() {
+        byte[] buffer = new byte[16];
+        final SecureRandom secureRandom = new SecureRandom();
+        secureRandom.nextBytes(buffer);
+        return buffer;
+    }
+
+    private SecretKeyFactory getSecretKeyFactory() {
+        try {
+            return SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(PBKDF2_ALGORITHM + " algorithm not found", e);
+        }
+    }
 }