diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php
index b7b31b18b539a05fbc3b18e8d51e0dc45c072d02..73909a319fbc38f7ab3413e11bed68c7b6d5d21d 100644
--- a/apps/provisioning_api/lib/Controller/AUserData.php
+++ b/apps/provisioning_api/lib/Controller/AUserData.php
@@ -50,6 +50,7 @@ use OCP\User\Backend\ISetDisplayNameBackend;
 use OCP\User\Backend\ISetPasswordBackend;
 
 abstract class AUserData extends OCSController {
+	public const SCOPE_SUFFIX = 'Scope';
 
 	/** @var IUserManager */
 	protected $userManager;
@@ -86,12 +87,13 @@ abstract class AUserData extends OCSController {
 	 * creates a array with all user data
 	 *
 	 * @param string $userId
+	 * @param bool $includeScopes
 	 * @return array
 	 * @throws NotFoundException
 	 * @throws OCSException
 	 * @throws OCSNotFoundException
 	 */
-	protected function getUserData(string $userId): array {
+	protected function getUserData(string $userId, bool $includeScopes = false): array {
 		$currentLoggedInUser = $this->userSession->getUser();
 
 		$data = [];
@@ -114,7 +116,7 @@ abstract class AUserData extends OCSController {
 		}
 
 		// Get groups data
-		$userAccount = $this->accountManager->getUser($targetUserObject);
+		$userAccount = $this->accountManager->getAccount($targetUserObject);
 		$groups = $this->groupManager->getUserGroups($targetUserObject);
 		$gids = [];
 		foreach ($groups as $group) {
@@ -137,11 +139,26 @@ abstract class AUserData extends OCSController {
 		$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
 		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
 		$data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
+		if ($includeScopes) {
+			$data[IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope();
+		}
 		$data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
-		$data[IAccountManager::PROPERTY_PHONE] = $userAccount[IAccountManager::PROPERTY_PHONE]['value'];
-		$data[IAccountManager::PROPERTY_ADDRESS] = $userAccount[IAccountManager::PROPERTY_ADDRESS]['value'];
-		$data[IAccountManager::PROPERTY_WEBSITE] = $userAccount[IAccountManager::PROPERTY_WEBSITE]['value'];
-		$data[IAccountManager::PROPERTY_TWITTER] = $userAccount[IAccountManager::PROPERTY_TWITTER]['value'];
+		if ($includeScopes) {
+			$data[IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope();
+		}
+
+		foreach ([
+			IAccountManager::PROPERTY_PHONE,
+			IAccountManager::PROPERTY_ADDRESS,
+			IAccountManager::PROPERTY_WEBSITE,
+			IAccountManager::PROPERTY_TWITTER,
+		] as $propertyName) {
+			$property = $userAccount->getProperty($propertyName);
+			$data[$propertyName] = $property->getValue();
+			if ($includeScopes) {
+				$data[$propertyName . self::SCOPE_SUFFIX] = $property->getScope();
+			}
+		}
 		$data['groups'] = $gids;
 		$data['language'] = $this->l10nFactory->getUserLanguage($targetUserObject);
 		$data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php
index 1c9b63a01c32052ef7a5694ff5b28e92f0328ef1..eb0bf978df8e4bab8ce27ef64739c3251340c879 100644
--- a/apps/provisioning_api/lib/Controller/UsersController.php
+++ b/apps/provisioning_api/lib/Controller/UsersController.php
@@ -470,7 +470,13 @@ class UsersController extends AUserData {
 	 * @throws OCSException
 	 */
 	public function getUser(string $userId): DataResponse {
-		$data = $this->getUserData($userId);
+		$includeScopes = false;
+		$currentUser = $this->userSession->getUser();
+		if ($currentUser && $currentUser->getUID() === $userId) {
+			$includeScopes = true;
+		}
+
+		$data = $this->getUserData($userId, $includeScopes);
 		// getUserData returns empty array if not enough permissions
 		if (empty($data)) {
 			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
@@ -490,7 +496,7 @@ class UsersController extends AUserData {
 	public function getCurrentUser(): DataResponse {
 		$user = $this->userSession->getUser();
 		if ($user) {
-			$data = $this->getUserData($user->getUID());
+			$data = $this->getUserData($user->getUID(), true);
 			// rename "displayname" to "display-name" only for this call to keep
 			// the API stable.
 			$data['display-name'] = $data['displayname'];
@@ -552,6 +558,9 @@ class UsersController extends AUserData {
 				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
 			}
 
+			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
+			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
+
 			$permittedFields[] = 'password';
 			if ($this->config->getSystemValue('force_language', false) === false ||
 				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
@@ -567,6 +576,10 @@ class UsersController extends AUserData {
 			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
 			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
 			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
+			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
+			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
+			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
+			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
 
 			// If admin they can edit their own quota
 			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
@@ -671,6 +684,23 @@ class UsersController extends AUserData {
 					}
 				}
 				break;
+			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
+			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
+			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
+			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
+			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
+			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
+				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
+				$userAccount = $this->accountManager->getUser($targetUser);
+				if ($userAccount[$propertyName]['scope'] !== $value) {
+					$userAccount[$propertyName]['scope'] = $value;
+					try {
+						$this->accountManager->updateUser($targetUser, $userAccount, true);
+					} catch (\InvalidArgumentException $e) {
+						throw new OCSException('Invalid ' . $e->getMessage(), 102);
+					}
+				}
+				break;
 			default:
 				throw new OCSException('', 103);
 		}
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index ff3b04d83955c8e1de711abf5660217a1ce8a597..74ba53737cac8ec0a025f269c5297a4518b5b3ce 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -144,6 +144,37 @@ class AccountManager implements IAccountManager {
 			}
 		}
 
+		$allowedScopes = [
+			self::SCOPE_PRIVATE,
+			self::SCOPE_LOCAL,
+			self::SCOPE_FEDERATED,
+			self::SCOPE_PUBLISHED,
+			self::VISIBILITY_PRIVATE,
+			self::VISIBILITY_CONTACTS_ONLY,
+			self::VISIBILITY_PUBLIC,
+		];
+
+		// validate and convert scope values
+		foreach ($data as $propertyName => $propertyData) {
+			if (isset($propertyData['scope'])) {
+				if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) {
+					throw new \InvalidArgumentException('scope');
+				}
+
+				if (
+					$propertyData['scope'] === self::SCOPE_PRIVATE
+					&& ($propertyName === self::PROPERTY_DISPLAYNAME || $propertyName === self::PROPERTY_EMAIL)
+				) {
+					// v2-private is not available for these fields
+					throw new \InvalidArgumentException('scope');
+				}
+
+				// migrate scope values to the new format
+				// invalid scopes are mapped to a default value
+				$data[$propertyName]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']);
+			}
+		}
+
 		if (empty($userData)) {
 			$this->insertNewUser($user, $data);
 		} elseif ($userData !== $data) {
@@ -405,7 +436,7 @@ class AccountManager implements IAccountManager {
 			}
 
 			$query->setParameter('name', $propertyName)
-				->setParameter('value', $property['value']);
+				->setParameter('value', $property['value'] ?? '');
 			$query->execute();
 		}
 	}
diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php
index 4c75ad85414a587d70297c1daea5c2ad38deb0a2..850f39df9e3c7849eb200f68e79a6bc5536c182d 100644
--- a/lib/private/Accounts/AccountProperty.php
+++ b/lib/private/Accounts/AccountProperty.php
@@ -128,7 +128,7 @@ class AccountProperty implements IAccountProperty {
 		return $this->scope;
 	}
 
-	private function mapScopeToV2($scope) {
+	public static function mapScopeToV2($scope) {
 		if (strpos($scope, 'v2-') === 0) {
 			return $scope;
 		}