diff --git a/goofy-server/pom.xml b/goofy-server/pom.xml
index f961b8017a703c638de6722234134116ab0249a3..ed0add92cdc0104dbec37efe9d8078092efae85d 100644
--- a/goofy-server/pom.xml
+++ b/goofy-server/pom.xml
@@ -118,6 +118,10 @@
 			<groupId>de.itvsh.kop.common</groupId>
 			<artifactId>kop-common-pdf</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>de.itvsh.kop.user</groupId>
+			<artifactId>user-manager-interface</artifactId>
+		</dependency>
 		
 		<!-- tools -->
 		<dependency>
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/RootController.java b/goofy-server/src/main/java/de/itvsh/goofy/RootController.java
index 6c92d0e8d98d4630d77d6f1c3abe94c74a2a9378..a069ce8261bddc575d47c03d52d18f0418f35fe6 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/RootController.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/RootController.java
@@ -26,6 +26,7 @@ package de.itvsh.goofy;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
 
 import java.time.Instant;
+import java.util.Objects;
 import java.util.Optional;
 
 import org.springframework.beans.factory.annotation.Autowired;
@@ -41,8 +42,8 @@ import de.itvsh.goofy.common.downloadtoken.DownloadTokenController;
 import de.itvsh.goofy.common.user.CurrentUserService;
 import de.itvsh.goofy.common.user.UserId;
 import de.itvsh.goofy.common.user.UserManagerUrlProvider;
-import de.itvsh.goofy.common.user.UserRemoteService;
 import de.itvsh.goofy.common.user.UserRole;
+import de.itvsh.goofy.common.user.UserService;
 import de.itvsh.goofy.system.SystemStatusService;
 import de.itvsh.goofy.vorgang.VorgangController;
 
@@ -69,40 +70,35 @@ public class RootController {
 	@Autowired
 	private SystemStatusService systemStatusService;
 	@Autowired
-	private UserRemoteService internalUserIdService;
+	private UserService userService;
 
 	@Autowired
 	private UserManagerUrlProvider userManagerUrlProvider;
 
 	@GetMapping
 	public EntityModel<RootResource> getRootResource() {
-		var internalUserId = internalUserIdService.getUserId(currentUserService.getUserId());
+		var internalUserId = userService.getInternalId(currentUserService.getUserId());
 
-		var modelBuilder = ModelBuilder.fromEntity(new RootResource())
+		return ModelBuilder.fromEntity(new RootResource())
 				.ifMatch(this::hasRole).addLinks(
 						linkTo(RootController.class).withSelfRel(),
 						linkTo(VorgangController.class).withRel(REL_VORGAENGE),
 						linkTo(DownloadTokenController.class).withRel(REL_DOWNLOAD_TOKEN),
 						Link.of(userManagerUrlProvider.getUserProfileSearchTemplate()
-										.queryParam(USER_PROFILE_SEARCH_DELETED_PARAM, false)
-										.build(false)
-										.toUriString(),
+								.queryParam(USER_PROFILE_SEARCH_DELETED_PARAM, false)
+								.build(false)
+								.toUriString(),
 								REL_SEARCH_USER))
 				.ifMatch(this::hasRoleAndSearchServerAvailable).addLinks(
-						buildVorgangListByPageLink(REL_SEARCH, Optional.empty()));
-
-		internalUserId.ifPresent(userId -> modelBuilder
-				.ifMatch(this::hasVerwaltungRole).addLink(
-						buildVorgangListByPageLink(REL_MY_VORGAENGE, Optional.of(userId)))
-				.ifMatch(this::hasVerwaltungRoleAndSearchServerAvailable).addLink(
-						buildVorgangListByPageLink(REL_SEARCH_MY_VORGAENGE, Optional.of(userId))));
-
-		var model = modelBuilder.buildModel();
-
-		getUserProfilesUrl()
-				.ifPresent(urlTemplate -> model.add(Link.of(String.format(urlTemplate, currentUserService.getUserId()), REL_CURRENT_USER)));
-
-		return model;
+						buildVorgangListByPageLink(REL_SEARCH, Optional.empty()))
+				.ifMatch(() -> Objects.nonNull(internalUserId) && hasVerwaltungRole())
+				.addLink(() -> buildVorgangListByPageLink(REL_MY_VORGAENGE, Optional.of(internalUserId)))
+				.ifMatch(() -> Objects.nonNull(internalUserId) && hasVerwaltungRoleAndSearchServerAvailable())
+				.addLink(() -> buildVorgangListByPageLink(REL_SEARCH_MY_VORGAENGE, Optional.of(internalUserId)))
+				.ifMatch(userManagerUrlProvider::isConfiguredForUserProfile)
+				.addLink(() -> Link.of(String.format(userManagerUrlProvider.getUserProfileTemplate(), currentUserService.getUserId()),
+						REL_CURRENT_USER))
+				.buildModel();
 	}
 
 	private boolean hasRoleAndSearchServerAvailable() {
@@ -126,14 +122,6 @@ public class RootController {
 		return linkTo(methodOn(VorgangController.class).getVorgangListByPage(0, null, null, assignedTo)).withRel(linkRel);
 	}
 
-	Optional<String> getUserProfilesUrl() {
-		if (userManagerUrlProvider.isConfiguredForUserProfile()) {
-			return Optional.of(userManagerUrlProvider.getUserProfileTemplate());
-		}
-
-		return Optional.empty();
-	}
-
 	class RootResource {
 
 		public String getVersion() {
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java
index bc6076da1206786609275e87069265e5bbef0dca..9c5333d0dd19f9336f94a96b8e5110b149db3e9a 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java
@@ -55,7 +55,7 @@ import lombok.extern.log4j.Log4j2;
 
 @ControllerAdvice
 @Log4j2
-@Order(99)
+@Order(97)
 public class ExceptionController {
 
 	private static final Set<String> IGNORABLE_CONSTRAINT_VIOLATION_ATTRIBUTES = new HashSet<>(Arrays.asList("groups", "payload", "message"));
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfileMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfileMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5711c71ef1f9fd4980c18bbd8b9e4c002c8e1fb9
--- /dev/null
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfileMapper.java
@@ -0,0 +1,15 @@
+package de.itvsh.goofy.common.user;
+
+import org.mapstruct.Mapper;
+
+import de.itvsh.kop.user.userprofile.GrpcUserProfile;
+
+@Mapper
+interface UserProfileMapper {
+
+	UserProfile mapFrom(GrpcUserProfile userProfile);
+
+	default UserId toUserId(String userId) {
+		return UserId.from(userId);
+	}
+}
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java
index daf0de0433d00666402be4ad21c067a8d2a46db5..e46db2510839fa2a8aa2dcb5b3ae0e90b134ebed 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java
@@ -23,110 +23,30 @@
  */
 package de.itvsh.goofy.common.user;
 
-import java.util.LinkedHashMap;
-import java.util.Optional;
-import java.util.function.Supplier;
-
-import org.apache.commons.lang3.StringUtils;
-import org.keycloak.KeycloakPrincipal;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.stereotype.Service;
 
-import de.itvsh.goofy.common.errorhandling.MessageCode;
-import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException;
-import lombok.extern.log4j.Log4j2;
+import de.itvsh.kop.user.grpc.userprofile.GrpcGetUserProfileRequest;
+import de.itvsh.kop.user.grpc.userprofile.UserProfileServiceGrpc.UserProfileServiceBlockingStub;
+import net.devh.boot.grpc.client.inject.GrpcClient;
 
-@Log4j2
-@Component
+@Service
 public class UserRemoteService {
 
-	static final String FIRST_NAME_KEY = "firstName";
-	static final String LAST_NAME_KEY = "lastName";
+	private static final String USER_MANAGER_GRPC_CLIENT = "user-manager";
 
+	@GrpcClient(USER_MANAGER_GRPC_CLIENT)
+	private UserProfileServiceBlockingStub userServiceStub;
 	@Autowired
-	private UserManagerProperties userManagerProperties;
-	@Autowired
-	private UserManagerUrlProvider userManagerUrlProvider;
-
-	private RestTemplate restTemplate = new RestTemplate();
-
-	public Optional<UserId> getUserId(UserId externalUserId) {
-		try {
-			if (userManagerUrlProvider.isConfiguredForInternalUserId()) {
-				var internalId = restTemplate.getForObject(userManagerProperties.getFullInternalUrlTemplate(), String.class,
-						externalUserId.toString());
-				return StringUtils.isNotEmpty(internalId) ? Optional.of(UserId.from(internalId)) : Optional.empty();
-			} else {
-				return Optional.empty();
-			}
-		} catch (RestClientException e) {
-			LOG.warn("Error loading internal Userid.", e);
-			return Optional.empty();
-		}
-	}
-
-	public UserProfile getUser(UserId userId) {
-		return executeHandlingException(() -> getUserById(userId));
-	}
-
-	private <T> T executeHandlingException(Supplier<T> runnable) {
-		try {
-			return runnable.get();
-		} catch (HttpClientErrorException e) {
-			if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
-				return null;
-			}
-			LOG.error("HttpClientErrorException: Error getting User by id.", e);
-			throw new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, e);
-		} catch (IllegalArgumentException e) {
-			LOG.error("IllegalArgumentException: Error getting User by id.", e);
-			throw new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, e);
-		}
-	}
+	private UserProfileMapper mapper;
 
-	private UserProfile getUserById(UserId userId) {
-		return buildUser(getBodyMap(doExchange(userId)));
-	}
-
-	private ResponseEntity<Object> doExchange(UserId userId) {
-		return restTemplate.exchange(buildUserProfileUri(userId), HttpMethod.GET, buildHttpEntityWithAuthorization(), Object.class);
-	}
-
-	String buildUserProfileUri(UserId userId) {
-		return UriComponentsBuilder.fromUriString(String.format(userManagerUrlProvider.getInternalUserProfileTemplate(), userId.toString()))
-				.toUriString();
-	}
-
-	private HttpEntity<Object> buildHttpEntityWithAuthorization() {
-		var headers = new HttpHeaders();
-		headers.add("Authorization", "Bearer " + getToken());
-		return new HttpEntity<>(headers);
-	}
-
-	String getToken() {
-		var principle = (KeycloakPrincipal<?>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
-		return principle.getKeycloakSecurityContext().getTokenString();
-	}
+	public UserProfile getById(UserId externalUserId) {
+		var response = userServiceStub.getById(buildRequest(externalUserId));
 
-	@SuppressWarnings("unchecked")
-	<T> LinkedHashMap<String, Object> getBodyMap(ResponseEntity<T> responseEntity) {
-		return (LinkedHashMap<String, Object>) responseEntity.getBody();
+		return mapper.mapFrom(response.getUserProfile());
 	}
 
-	UserProfile buildUser(LinkedHashMap<String, Object> bodyMap) {
-		return UserProfile.builder()
-				.firstName((String) bodyMap.getOrDefault(FIRST_NAME_KEY, StringUtils.EMPTY))
-				.lastName((String) bodyMap.getOrDefault(LAST_NAME_KEY, StringUtils.EMPTY))
-				.build();
+	private GrpcGetUserProfileRequest buildRequest(UserId externalUserId) {
+		return GrpcGetUserProfileRequest.newBuilder().setUserId(externalUserId.toString()).build();
 	}
-}
+}
\ No newline at end of file
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java
index 4c970317e1433ca620e90cb9c83971987b1bdde6..419a83dd8ae7a643cc92f59b97be681a8767a8bc 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java
@@ -26,6 +26,13 @@ package de.itvsh.goofy.common.user;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import de.itvsh.goofy.common.errorhandling.MessageCode;
+import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException;
+import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
 @Service
 public class UserService {
 
@@ -33,6 +40,27 @@ public class UserService {
 	private UserRemoteService remoteService;
 
 	public UserProfile getById(UserId userId) {
-		return remoteService.getUser(userId);
+		try {
+			return remoteService.getById(userId);
+		} catch (StatusRuntimeException e) {
+			if (isNotFound(e)) {
+				return null;
+			}
+			throw new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, e);
+		}
+	}
+
+	private boolean isNotFound(StatusRuntimeException e) {
+		return e.getStatus().getCode().value() == Status.NOT_FOUND.getCode().value();
+	}
+
+	public UserId getInternalId(UserId externalId) {
+		try {
+			var userProfile = remoteService.getById(externalId);
+			return userProfile.getId();
+		} catch (Exception e) {// TODO pruefen welche Exception wirklich zurueckgegeben wird
+			LOG.warn("Error loading internal Userid.", e);
+			return null;
+		}
 	}
 }
\ No newline at end of file
diff --git a/goofy-server/src/main/resources/application-remotekc.yml b/goofy-server/src/main/resources/application-remotekc.yml
index a9c24daa3d8cf653c70c5e9700fae348d1cc5882..da5ee99125b5053ab81270ee622e5254d3b1d984 100644
--- a/goofy-server/src/main/resources/application-remotekc.yml
+++ b/goofy-server/src/main/resources/application-remotekc.yml
@@ -3,4 +3,4 @@ keycloak:
   resource: sh-kiel-dev-goofy
   public-client: true
   use-resource-role-mappings: true
-  auth-server-url: https://sso.dev.ozg-sh.de
+  auth-server-url: https://sso.dev.by.kop-cloud.de
diff --git a/goofy-server/src/main/resources/application.yml b/goofy-server/src/main/resources/application.yml
index dd6db3af8b0d9e7ae616277a31cb78b5c15a114d..38e8bd89fc4d9eb2a2ab8d6a10bc80e876cd145b 100644
--- a/goofy-server/src/main/resources/application.yml
+++ b/goofy-server/src/main/resources/application.yml
@@ -63,6 +63,9 @@ grpc:
     pluto:
       address: static://127.0.0.1:9090
       negotiationType: PLAINTEXT
+    user-manager:
+      address: static://127.0.0.1:9000
+      negotiationType: PLAINTEXT 
 
 kop:
   auth:
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java
index 9dab37319a45efd8fd17c1515078fe4aac062021..4a51d2665c108e6181fab821e45f39df0bd4e93b 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java
@@ -31,7 +31,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
-import java.util.Optional;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -46,15 +45,15 @@ import org.springframework.hateoas.Link;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.util.UriComponentsBuilder;
 
 import de.itvsh.goofy.common.user.CurrentUserService;
 import de.itvsh.goofy.common.user.UserId;
 import de.itvsh.goofy.common.user.UserManagerUrlProvider;
 import de.itvsh.goofy.common.user.UserProfileTestFactory;
-import de.itvsh.goofy.common.user.UserRemoteService;
 import de.itvsh.goofy.common.user.UserRole;
+import de.itvsh.goofy.common.user.UserService;
 import de.itvsh.goofy.system.SystemStatusService;
-import org.springframework.web.util.UriComponentsBuilder;
 
 class RootControllerTest {
 
@@ -68,7 +67,7 @@ class RootControllerTest {
 	@Mock
 	private SystemStatusService systemStatusService;
 	@Mock
-	private UserRemoteService internalUserIdService;
+	private UserService userService;
 	@Mock
 	private UserManagerUrlProvider userManagerUrlProvider;
 
@@ -79,7 +78,7 @@ class RootControllerTest {
 		mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
 
 		when(currentUserService.getUserId()).thenReturn(UserId.from("42"));
-		when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty());
+		when(userService.getInternalId(any())).thenReturn(null);
 		when(userManagerUrlProvider.getUserProfileSearchTemplate()).thenReturn(
 				UriComponentsBuilder.fromUriString("UserProfileSearchTemplateDummy/$"));
 	}
@@ -123,7 +122,7 @@ class RootControllerTest {
 
 			@Test
 			void shouldHaveMyVorgaengeLink() {
-				when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID));
+				when(userService.getInternalId(any())).thenReturn(UserProfileTestFactory.ID);
 
 				var model = controller.getRootResource();
 
@@ -133,7 +132,7 @@ class RootControllerTest {
 
 			@Test
 			void shouldHaveSearchMyVorgaengeLink() {
-				when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID));
+				when(userService.getInternalId(any())).thenReturn(UserProfileTestFactory.ID);
 
 				var model = controller.getRootResource();
 
@@ -167,7 +166,7 @@ class RootControllerTest {
 
 				@Test
 				void shouldHaveMyVorgaengeLink() {
-					when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID));
+					when(userService.getInternalId(any())).thenReturn(UserProfileTestFactory.ID);
 
 					var model = controller.getRootResource();
 
@@ -293,7 +292,7 @@ class RootControllerTest {
 				void init() {
 					doReturn(true).when(controller).hasVerwaltungRole();
 
-					when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty());
+					when(userService.getInternalId(any())).thenReturn(null);
 					when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false);
 				}
 
@@ -340,7 +339,7 @@ class RootControllerTest {
 		@BeforeEach
 		void initTest() {
 			when(currentUserService.getUserId()).thenReturn(UserId.from("42"));
-			when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty());
+			when(userService.getInternalId(any())).thenReturn(null);
 			when(userManagerUrlProvider.getUserProfileSearchTemplate()).thenReturn(
 					UriComponentsBuilder.fromUriString("UserProfileSearchTemplateDummy/$"));
 		}
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserProfileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserProfileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c2e08459a58128c2f5e46552831ac451f0c043a
--- /dev/null
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserProfileTestFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.itvsh.goofy.common.user;
+
+import de.itvsh.kop.user.grpc.userprofile.GrpcGetUserProfileRequest;
+import de.itvsh.kop.user.grpc.userprofile.GrpcGetUserProfileResponse;
+import de.itvsh.kop.user.userprofile.GrpcUserProfile;
+
+public class GrpcUserProfileTestFactory {
+
+	public static GrpcGetUserProfileRequest createGetUserProfileRequest() {
+		return GrpcGetUserProfileRequest.newBuilder().setUserId(UserProfileTestFactory.ID.toString()).build();
+	}
+
+	public static GrpcGetUserProfileResponse createGetUserProfileResponse() {
+		return GrpcGetUserProfileResponse.newBuilder().setUserProfile(create()).build();
+	}
+
+	public static GrpcUserProfile create() {
+		return createBuilder().build();
+	}
+
+	public static GrpcUserProfile.Builder createBuilder() {
+		return GrpcUserProfile.newBuilder()
+				.setId(UserProfileTestFactory.ID.toString())
+				.setFirstName(UserProfileTestFactory.FIRSTNAME)
+				.setLastName(UserProfileTestFactory.LASTNAME);
+	}
+}
\ No newline at end of file
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java
index d4aa272f6699fc73f3595545e4ebce88dcbd44f3..8b749807f1b82e50029c06dc1d836374d76061bd 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java
@@ -1,26 +1,3 @@
-/*
- * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
 package de.itvsh.goofy.common.user;
 
 import java.util.Collections;
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e89b0e8311fa67b0cea66656d09fd392670b823
--- /dev/null
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileMapperTest.java
@@ -0,0 +1,51 @@
+package de.itvsh.goofy.common.user;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+
+import de.itvsh.kop.user.userprofile.GrpcUserProfile;
+
+class UserProfileMapperTest {
+
+	private UserProfileMapper mapper = Mappers.getMapper(UserProfileMapper.class);
+
+	@DisplayName("Map from")
+	@Nested
+	class TestMapFrom {
+
+		@Test
+		void shouldMapFirstName() {
+			var userProfile = map();
+
+			assertThat(userProfile.getFirstName()).isEqualTo(UserProfileTestFactory.FIRSTNAME);
+		}
+
+		@Test
+		void shouldMapLastName() {
+			var userProfile = map();
+
+			assertThat(userProfile.getLastName()).isEqualTo(UserProfileTestFactory.LASTNAME);
+		}
+
+		@Test
+		void shouldMapId() {
+			var userProfile = map();
+
+			assertThat(userProfile.getId()).isEqualTo(UserProfileTestFactory.ID);
+		}
+
+		private UserProfile map() {
+			return mapper.mapFrom(GrpcUserProfileTestFactory.create());
+		}
+
+		@Test
+		void shouldProceedWithNull() {
+			Assertions.assertDoesNotThrow(() -> mapper.mapFrom(GrpcUserProfile.newBuilder().build()));
+		}
+	}
+}
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java
index 5d31612f2a7ed0d0b52cf2b09407bf0c9030d035..930e8c3639015f053e515313ba388f00587f2873 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java
@@ -27,26 +27,18 @@ import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
-import java.util.LinkedHashMap;
-import java.util.Map;
-
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
 
-import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException;
+import de.itvsh.kop.user.grpc.userprofile.GrpcGetUserProfileRequest;
+import de.itvsh.kop.user.grpc.userprofile.UserProfileServiceGrpc.UserProfileServiceBlockingStub;
 
 class UserRemoteServiceTest {
 
@@ -54,208 +46,43 @@ class UserRemoteServiceTest {
 	@InjectMocks
 	private UserRemoteService service;
 	@Mock
-	private UserManagerProperties userManagerProperties;
-	@Mock
-	private UserManagerUrlProvider userManagerUrlProvider;
-
+	private UserProfileServiceBlockingStub serviceStub;
 	@Mock
-	private RestTemplate restTemplate;
-
-	@DisplayName("Get userId")
-	@Nested
-	class TestGetUserId {
-
-		private final String internalUrlTemplate = "DummyInternalUrlTemplate";
-
-		@DisplayName("with configured usermanager")
-		@Nested
-		class TestWithConfiguredUserManager {
-
-			@BeforeEach
-			void mock() {
-				when(userManagerProperties.getFullInternalUrlTemplate()).thenReturn(internalUrlTemplate);
-				when(userManagerUrlProvider.isConfiguredForInternalUserId()).thenReturn(true);
-			}
-
-			@DisplayName("on valid response")
-			@Nested
-			class TestSuccess {
-
-				@BeforeEach
-				void mock() {
-					when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenReturn(UserProfileTestFactory.ID.toString());
-				}
-
-				@Test
-				void shouldReturnResponseAsUserId() {
-					var userId = service.getUserId(UserProfileTestFactory.ID);
-
-					assertThat(userId).hasValue(UserProfileTestFactory.ID);
-				}
-			}
-
-			@DisplayName("on error response")
-			@Nested
-			class TestErrorCases {
-
-				@Test
-				void shouldHandleEmptyValue() {
-					when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenReturn("");
-
-					var res = service.getUserId(UserProfileTestFactory.ID);
-
-					assertThat(res).isNotPresent();
-				}
-
-				@Test
-				void shouldHandleError() {
-					when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenThrow(new RestClientException("Test error"));
-
-					var res = service.getUserId(UserProfileTestFactory.ID);
-
-					assertThat(res).isNotPresent();
-				}
-			}
-		}
-
-		@DisplayName("with not configured usermanager")
-		@Nested
-		class TestOnNotConfiguredUserManager {
-
-			@BeforeEach
-			void mock() {
-				when(userManagerUrlProvider.isConfiguredForInternalUserId()).thenReturn(false);
-			}
-
-			@Test
-			void shouldNotCallUserManagerProperties() {
-				service.getUserId(UserProfileTestFactory.ID);
-
-				verify(userManagerProperties, never()).getFullInternalUrlTemplate();
-			}
-
-			@Test
-			void shouldReturnEmptyOptional() {
-				var user = service.getUserId(UserProfileTestFactory.ID);
-
-				assertThat(user).isNotPresent();
-			}
-		}
-	}
+	private UserProfileMapper mapper;
 
-	@DisplayName("Get user")
+	@DisplayName("Find by id")
 	@Nested
-	class TestGetUser {
+	class TestFindById {
 
-		private final String profileUri = "DummyProfileTemplate/" + UserProfileTestFactory.ID;
-		private final String dummyToken = "Token";
+		@Captor
+		private ArgumentCaptor<GrpcGetUserProfileRequest> requestCaptor;
 
 		@BeforeEach
 		void mock() {
-			doReturn(profileUri).when(service).buildUserProfileUri(any());
-			doReturn(dummyToken).when(service).getToken();
+			when(serviceStub.getById(any())).thenReturn(GrpcUserProfileTestFactory.createGetUserProfileResponse());
+			when(mapper.mapFrom(any())).thenReturn(UserProfileTestFactory.create());
 		}
 
-		@DisplayName("on valid response")
-		@Nested
-		class TestOnValidResponse {
-
-			private final Map<String, Object> bodyMap = new LinkedHashMap<>(Map.of(UserRemoteService.FIRST_NAME_KEY, UserProfileTestFactory.FIRSTNAME,
-					UserRemoteService.LAST_NAME_KEY, UserProfileTestFactory.LASTNAME));
-			private final ResponseEntity<Object> response = new ResponseEntity<>(bodyMap, HttpStatus.OK);
-
-			@BeforeEach
-			void mock() {
-				when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenReturn(response);
-			}
-
-			@Test
-			void shouldCallRestTemplate() {
-				var headers = new HttpHeaders();
-				headers.add("Authorization", "Bearer " + dummyToken);
-				var httpEntity = new HttpEntity<>(headers);
-
-				service.getUser(UserProfileTestFactory.ID);
-
-				verify(restTemplate).exchange(profileUri, HttpMethod.GET, httpEntity, Object.class);
-			}
-
-			@Test
-			void shouldBuildUrl() {
-				service.getUser(UserProfileTestFactory.ID);
-
-				verify(service).buildUserProfileUri(UserProfileTestFactory.ID);
-			}
-
-			@Test
-			void shouldReturnUser() {
-				var loadedUser = service.getUser(UserProfileTestFactory.ID);
-
-				assertThat(loadedUser.getFirstName()).isEqualTo(UserProfileTestFactory.FIRSTNAME);
-				assertThat(loadedUser.getLastName()).isEqualTo(UserProfileTestFactory.LASTNAME);
-			}
-		}
-
-		@DisplayName("on error response")
-		@Nested
-		class TestOnErrorResponse {
-
-			private final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE,
-					"Test error");
-			private final IllegalArgumentException illegalArgumentException = new IllegalArgumentException();
-
-			private final HttpClientErrorException notFoundException = new HttpClientErrorException(HttpStatus.NOT_FOUND, "Test error");
-
-			@Test
-			void shouldThrowServiceUnavailablExceptionOnException() {
-				when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(httpClientErrorException);
-
-				assertThatThrownBy(() -> service.getUser(UserProfileTestFactory.ID)).isInstanceOf(ServiceUnavailableException.class)
-						.hasCause(httpClientErrorException);
-			}
-
-			@Test
-			void shouldThrowServiceUnavailablExceptionOnIlleglaArgumentException() {
-				when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(illegalArgumentException);
-
-				assertThatThrownBy(() -> service.getUser(UserProfileTestFactory.ID)).isInstanceOf(ServiceUnavailableException.class)
-						.hasCause(illegalArgumentException);
-			}
-
-			@Test
-			void shouldReturnEmptyOptionalOnNotFoundException() {
-				when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(notFoundException);
-
-				var user = service.getUser(UserProfileTestFactory.ID);
-
-				assertThat(user).isNull();
-			}
-		}
-	}
-
-	@DisplayName("Build user profile uri")
-	@Nested
-	class TestBuildUserProfileUri {
-
-		private final String profileUriTemplate = "DummyProfileTemplate/%s";
+		@Test
+		void shouldCallRemoteService() {
+			service.getById(UserProfileTestFactory.ID);
 
-		@BeforeEach
-		void mock() {
-			when(userManagerUrlProvider.getInternalUserProfileTemplate()).thenReturn(profileUriTemplate);
+			verify(serviceStub).getById(requestCaptor.capture());
+			assertThat(requestCaptor.getValue().getUserId()).isEqualTo(UserProfileTestFactory.ID.toString());
 		}
 
 		@Test
-		void shouldCallUserManagerUrlProvider() {
-			service.buildUserProfileUri(UserProfileTestFactory.ID);
+		void shouldCallMapper() {
+			service.getById(UserProfileTestFactory.ID);
 
-			verify(userManagerUrlProvider).getInternalUserProfileTemplate();
+			verify(mapper).mapFrom(GrpcUserProfileTestFactory.create());
 		}
 
 		@Test
-		void shouldReturnUserProfileUri() {
-			var uri = service.buildUserProfileUri(UserProfileTestFactory.ID);
+		void shouldReturnValue() {
+			var userProfile = service.getById(UserProfileTestFactory.ID);
 
-			assertThat(uri).isEqualTo("DummyProfileTemplate/" + UserProfileTestFactory.ID);
+			assertThat(userProfile).usingRecursiveComparison().isEqualTo(UserProfileTestFactory.create());
 		}
 	}
 }
\ No newline at end of file
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java
index d8092e887d3b4bd0bb20b48a159fa994a3014915..7d242ba7f6504b53d909aeae65e803891a91f32c 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java
@@ -23,14 +23,24 @@
  */
 package de.itvsh.goofy.common.user;
 
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.EnumSource.Mode;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
+import de.itvsh.goofy.common.errorhandling.MessageCode;
+import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException;
+import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+
 class UserServiceTest {
 
 	@InjectMocks
@@ -44,9 +54,58 @@ class UserServiceTest {
 
 		@Test
 		void shouldCallRemoteService() {
-			service.getById(UserProfileTestFactory.ID);
+			getById();
+
+			verify(remoteService).getById(UserProfileTestFactory.ID);
+		}
+
+		@Test
+		void shouldReturnNullOnNotFoundException() {
+			when(remoteService.getById(any())).thenThrow(new StatusRuntimeException(Status.NOT_FOUND));
+
+			var userProfile = getById();
+
+			assertThat(userProfile).isNull();
+		}
+
+		@DisplayName("should throw unavailable exception if exception is other than not found")
+		@ParameterizedTest
+		@EnumSource(mode = Mode.EXCLUDE, names = { "NOT_FOUND" })
+		void shouldThrowServiceUnavailableException(Status.Code status) {
+			when(remoteService.getById(any())).thenThrow(new StatusRuntimeException(Status.fromCode(status)));
+
+			assertThatThrownBy(() -> getById())
+					.isInstanceOf(ServiceUnavailableException.class)
+					.extracting(e -> ((ServiceUnavailableException) e).getMessageCode()).isEqualTo(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE);
+		}
+
+		private UserProfile getById() {
+			return service.getById(UserProfileTestFactory.ID);
+		}
+	}
+
+	@DisplayName("Get internalId")
+	@Nested
+	class TestGetInternalId {
+
+		@Test
+		void shouldCallRemoteService() {
+			getInternalId();
+
+			verify(remoteService).getById(UserProfileTestFactory.ID);
+		}
+
+		@Test
+		void shouldReturnNullOnException() {
+			when(remoteService.getById(any())).thenThrow(new StatusRuntimeException(Status.UNAVAILABLE));
+
+			var userProfile = getInternalId();
+
+			assertThat(userProfile).isNull();
+		}
 
-			verify(remoteService).getUser(UserProfileTestFactory.ID);
+		private UserId getInternalId() {
+			return service.getInternalId(UserProfileTestFactory.ID);
 		}
 	}
-}
+}
\ No newline at end of file
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java
index a4ada1e59fa75268a52d42cef5e90924d605d3c3..2b0134f6bbc063569dad88718fcbf952447a2da8 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java
@@ -64,7 +64,7 @@ class PostfachNachrichtPdfServiceITCase {
 
 		@BeforeEach
 		void mock() {
-			when(userRemoteService.getUser(any(UserId.class))).thenReturn(UserProfileTestFactory.create());
+			when(userRemoteService.getById(any(UserId.class))).thenReturn(UserProfileTestFactory.create());
 		}
 
 		@SneakyThrows
diff --git a/pom.xml b/pom.xml
index 9471c424726c22a57a10a9d937205eaefdb39015..12c913062f23938e1e3ecff70563c6f6cab83891 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
 	<parent>
 		<groupId>de.itvsh.kop.common</groupId>
 		<artifactId>kop-common-parent</artifactId>
-		<version>1.3.0</version>
+		<version>1.4.0-SNAPSHOT</version>
 	</parent>
 
 	<modules>
@@ -53,6 +53,7 @@
 		<pluto.version>1.3.0-SNAPSHOT</pluto.version>
 		<jsoup.version>1.15.1</jsoup.version>
 		<kop-common-pdf.version>1.3.0</kop-common-pdf.version>
+		<user-manager.version>1.3.0-SNAPSHOT</user-manager.version>
 	</properties>
 	
 	<build>
@@ -114,6 +115,21 @@
 				<artifactId>jsoup</artifactId>
 				<version>${jsoup.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>de.itvsh.kop.user</groupId>
+				<artifactId>user-manager-interface</artifactId>
+				<version>${user-manager.version}</version>
+				<exclusions>
+					<exclusion>
+						<groupId>io.grpc</groupId>
+						<artifactId>grpc-core</artifactId>
+					</exclusion>
+					<exclusion>
+						<groupId>org.jboss.slf4j</groupId>
+						<artifactId>slf4j-jboss-logmanager</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
 		</dependencies>
 	</dependencyManagement>