diff --git a/3rdparty b/3rdparty
index e287243b8eeaf72443b841ace3b1788e85a5aacd..27a56c5bb9d0ec514a8fb22044fd5f03a51ea2a5 160000
--- a/3rdparty
+++ b/3rdparty
@@ -1 +1 @@
-Subproject commit e287243b8eeaf72443b841ace3b1788e85a5aacd
+Subproject commit 27a56c5bb9d0ec514a8fb22044fd5f03a51ea2a5
diff --git a/apps/dav/lib/CardDAV/Converter.php b/apps/dav/lib/CardDAV/Converter.php
index dd7ceb77c55f6ef8faddc560fbe9107075a97924..8602d3b4ed901dd39bc182868bb1f0a122a86b5f 100644
--- a/apps/dav/lib/CardDAV/Converter.php
+++ b/apps/dav/lib/CardDAV/Converter.php
@@ -26,6 +26,7 @@
 namespace OCA\DAV\CardDAV;
 
 use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
 use OCP\IImage;
 use OCP\IUser;
 use Sabre\VObject\Component\VCard;
@@ -62,8 +63,8 @@ class Converter {
 
 		$publish = false;
 
-		if ($image !== null && isset($userData[AccountManager::PROPERTY_AVATAR])) {
-			$userData[AccountManager::PROPERTY_AVATAR]['value'] = true;
+		if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
+			$userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
 		}
 
 		foreach ($userData as $property => $value) {
@@ -76,28 +77,28 @@ class Converter {
 			if ($shareWithTrustedServers && !$emptyValue) {
 				$publish = true;
 				switch ($property) {
-					case AccountManager::PROPERTY_DISPLAYNAME:
+					case IAccountManager::PROPERTY_DISPLAYNAME:
 						$vCard->add(new Text($vCard, 'FN', $value['value']));
 						$vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
 						break;
-					case AccountManager::PROPERTY_AVATAR:
+					case IAccountManager::PROPERTY_AVATAR:
 						if ($image !== null) {
 							$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
 						}
 						break;
-					case AccountManager::PROPERTY_EMAIL:
+					case IAccountManager::PROPERTY_EMAIL:
 						$vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
 						break;
-					case AccountManager::PROPERTY_WEBSITE:
+					case IAccountManager::PROPERTY_WEBSITE:
 						$vCard->add(new Text($vCard, 'URL', $value['value']));
 						break;
-					case AccountManager::PROPERTY_PHONE:
+					case IAccountManager::PROPERTY_PHONE:
 						$vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
 						break;
-					case AccountManager::PROPERTY_ADDRESS:
+					case IAccountManager::PROPERTY_ADDRESS:
 						$vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
 						break;
-					case AccountManager::PROPERTY_TWITTER:
+					case IAccountManager::PROPERTY_TWITTER:
 						$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
 						break;
 				}
diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php
index 8b4125c6c68440a4b08213e8f7e790317770aa8c..aef5cf8ef1c429ec168a10c9aebe41852f2c228e 100644
--- a/apps/dav/tests/unit/CardDAV/ConverterTest.php
+++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php
@@ -29,13 +29,14 @@ namespace OCA\DAV\Tests\unit\CardDAV;
 
 use OC\Accounts\AccountManager;
 use OCA\DAV\CardDAV\Converter;
+use OCP\Accounts\IAccountManager;
 use OCP\IImage;
 use OCP\IUser;
 use Test\TestCase;
 
 class ConverterTest extends TestCase {
 
-	/** @var  AccountManager | \PHPUnit\Framework\MockObject\MockObject */
+	/** @var AccountManager|\PHPUnit\Framework\MockObject\MockObject */
 	private $accountManager;
 
 	protected function setUp(): void {
@@ -49,36 +50,36 @@ class ConverterTest extends TestCase {
 			->disableOriginalConstructor()->getMock();
 		$accountManager->expects($this->any())->method('getUser')->willReturn(
 			[
-				AccountManager::PROPERTY_DISPLAYNAME =>
+				IAccountManager::PROPERTY_DISPLAYNAME =>
 					[
 						'value' => $user->getDisplayName(),
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 					],
-				AccountManager::PROPERTY_ADDRESS =>
+				IAccountManager::PROPERTY_ADDRESS =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_WEBSITE =>
+				IAccountManager::PROPERTY_WEBSITE =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_EMAIL =>
+				IAccountManager::PROPERTY_EMAIL =>
 					[
 						'value' => $user->getEMailAddress(),
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 					],
-				AccountManager::PROPERTY_AVATAR =>
+				IAccountManager::PROPERTY_AVATAR =>
 					[
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
 					],
-				AccountManager::PROPERTY_PHONE =>
+				IAccountManager::PROPERTY_PHONE =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_TWITTER =>
+				IAccountManager::PROPERTY_TWITTER =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
diff --git a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
index 0a474d649bc23d3011de0db99a0d83e611ad0098..eb8186807c68bc6e699e2b2d092cba7110ba985d 100644
--- a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
@@ -31,6 +31,7 @@ namespace OCA\DAV\Tests\unit\CardDAV;
 use OC\Accounts\AccountManager;
 use OCA\DAV\CardDAV\CardDavBackend;
 use OCA\DAV\CardDAV\SyncService;
+use OCP\Accounts\IAccountManager;
 use OCP\ILogger;
 use OCP\IUser;
 use OCP\IUserManager;
@@ -132,36 +133,36 @@ class SyncServiceTest extends TestCase {
 		$accountManager = $this->getMockBuilder(AccountManager::class)->disableOriginalConstructor()->getMock();
 		$accountManager->expects($this->any())->method('getUser')
 			->willReturn([
-				AccountManager::PROPERTY_DISPLAYNAME =>
+				IAccountManager::PROPERTY_DISPLAYNAME =>
 					[
 						'value' => $user->getDisplayName(),
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 					],
-				AccountManager::PROPERTY_ADDRESS =>
+				IAccountManager::PROPERTY_ADDRESS =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_WEBSITE =>
+				IAccountManager::PROPERTY_WEBSITE =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_EMAIL =>
+				IAccountManager::PROPERTY_EMAIL =>
 					[
 						'value' => $user->getEMailAddress(),
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 					],
-				AccountManager::PROPERTY_AVATAR =>
+				IAccountManager::PROPERTY_AVATAR =>
 					[
 						'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
 					],
-				AccountManager::PROPERTY_PHONE =>
+				IAccountManager::PROPERTY_PHONE =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
 					],
-				AccountManager::PROPERTY_TWITTER =>
+				IAccountManager::PROPERTY_TWITTER =>
 					[
 						'value' => '',
 						'scope' => AccountManager::VISIBILITY_PRIVATE,
diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php
index fd1579ca8437cf48c12c7d352b1fce157c4801e5..912dd82e853550ed061541fc2aeb664f60dcdc2e 100644
--- a/apps/provisioning_api/appinfo/routes.php
+++ b/apps/provisioning_api/appinfo/routes.php
@@ -48,6 +48,7 @@ return [
 		// Users
 		['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
 		['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
+		['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
 		['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
 		['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
 		['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php
index 131db91add912c94dd051c4f9fbd9a2bc74a8a55..b7b31b18b539a05fbc3b18e8d51e0dc45c072d02 100644
--- a/apps/provisioning_api/lib/Controller/AUserData.php
+++ b/apps/provisioning_api/lib/Controller/AUserData.php
@@ -35,6 +35,7 @@ use OC\Accounts\AccountManager;
 use OC\User\Backend;
 use OC\User\NoUserException;
 use OC_Helper;
+use OCP\Accounts\IAccountManager;
 use OCP\AppFramework\OCS\OCSException;
 use OCP\AppFramework\OCS\OCSNotFoundException;
 use OCP\AppFramework\OCSController;
@@ -135,12 +136,12 @@ abstract class AUserData extends OCSController {
 		$data['backend'] = $targetUserObject->getBackendClassName();
 		$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
 		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
-		$data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
-		$data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
-		$data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
-		$data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
-		$data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
-		$data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
+		$data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
+		$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'];
 		$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 fd143a0e46663b628d1d4e5cf2f67fa82e44b109..735d394796b103bf6ef772c28f8597a70931a650 100644
--- a/apps/provisioning_api/lib/Controller/UsersController.php
+++ b/apps/provisioning_api/lib/Controller/UsersController.php
@@ -41,12 +41,18 @@ declare(strict_types=1);
 
 namespace OCA\Provisioning_API\Controller;
 
+use libphonenumber\NumberParseException;
+use libphonenumber\PhoneNumber;
+use libphonenumber\PhoneNumberFormat;
+use libphonenumber\PhoneNumberUtil;
 use OC\Accounts\AccountManager;
 use OC\Authentication\Token\RemoteWipe;
 use OC\HintException;
 use OCA\Provisioning_API\FederatedShareProviderFactory;
 use OCA\Settings\Mailer\NewUserMailHelper;
+use OCP\Accounts\IAccountManager;
 use OCP\App\IAppManager;
+use OCP\AppFramework\Http;
 use OCP\AppFramework\Http\DataResponse;
 use OCP\AppFramework\OCS\OCSException;
 use OCP\AppFramework\OCS\OCSForbiddenException;
@@ -55,6 +61,7 @@ use OCP\IGroup;
 use OCP\IGroupManager;
 use OCP\ILogger;
 use OCP\IRequest;
+use OCP\IURLGenerator;
 use OCP\IUser;
 use OCP\IUserManager;
 use OCP\IUserSession;
@@ -67,6 +74,8 @@ class UsersController extends AUserData {
 
 	/** @var IAppManager */
 	private $appManager;
+	/** @var IURLGenerator */
+	protected $urlGenerator;
 	/** @var ILogger */
 	private $logger;
 	/** @var IFactory */
@@ -90,6 +99,7 @@ class UsersController extends AUserData {
 								IGroupManager $groupManager,
 								IUserSession $userSession,
 								AccountManager $accountManager,
+								IURLGenerator $urlGenerator,
 								ILogger $logger,
 								IFactory $l10nFactory,
 								NewUserMailHelper $newUserMailHelper,
@@ -107,6 +117,7 @@ class UsersController extends AUserData {
 							$l10nFactory);
 
 		$this->appManager = $appManager;
+		$this->urlGenerator = $urlGenerator;
 		$this->logger = $logger;
 		$this->l10nFactory = $l10nFactory;
 		$this->newUserMailHelper = $newUserMailHelper;
@@ -201,6 +212,65 @@ class UsersController extends AUserData {
 		]);
 	}
 
+
+	/**
+	 * @NoAdminRequired
+	 * @NoSubAdminRequired
+	 *
+	 * @param string $location
+	 * @param array $search
+	 * @return DataResponse
+	 */
+	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
+		$phoneUtil = PhoneNumberUtil::getInstance();
+
+		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
+			// Not a valid region code
+			return new DataResponse([], Http::STATUS_BAD_REQUEST);
+		}
+
+		$normalizedNumberToKey = [];
+		foreach ($search as $key => $phoneNumbers) {
+			foreach ($phoneNumbers as $phone) {
+				try {
+					$phoneNumber = $phoneUtil->parse($phone, $location);
+					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
+						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
+						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
+					}
+				} catch (NumberParseException $e) {
+				}
+			}
+		}
+
+		$phoneNumbers = array_keys($normalizedNumberToKey);
+
+		if (empty($phoneNumbers)) {
+			return new DataResponse();
+		}
+
+		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
+
+		if (empty($userMatches)) {
+			return new DataResponse();
+		}
+
+		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
+		if (strpos($cloudUrl, 'http://') === 0) {
+			$cloudUrl = substr($cloudUrl, strlen('http://'));
+		} elseif (strpos($cloudUrl, 'https://') === 0) {
+			$cloudUrl = substr($cloudUrl, strlen('https://'));
+		}
+
+		$matches = [];
+		foreach ($userMatches as $phone => $userId) {
+			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
+			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
+		}
+
+		return new DataResponse($matches);
+	}
+
 	/**
 	 * @throws OCSException
 	 */
@@ -431,17 +501,17 @@ class UsersController extends AUserData {
 
 		// Editing self (display, email)
 		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
-			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
-			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
+			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
 		}
 
 		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
 			$shareProvider = $this->federatedShareProviderFactory->get();
 			if ($shareProvider->isLookupServerUploadEnabled()) {
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
+				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
+				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
 			}
 		}
 
@@ -474,8 +544,8 @@ class UsersController extends AUserData {
 			// Editing self (display, email)
 			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
 				$permittedFields[] = 'display';
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
+				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
 			}
 
 			$permittedFields[] = 'password';
@@ -492,10 +562,10 @@ class UsersController extends AUserData {
 			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
 				$shareProvider = $this->federatedShareProviderFactory->get();
 				if ($shareProvider->isLookupServerUploadEnabled()) {
-					$permittedFields[] = AccountManager::PROPERTY_PHONE;
-					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
-					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
-					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
+					$permittedFields[] = IAccountManager::PROPERTY_PHONE;
+					$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+					$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+					$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
 				}
 			}
 
@@ -510,15 +580,15 @@ class UsersController extends AUserData {
 			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
 				// They have permissions over the user
 				$permittedFields[] = 'display';
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
+				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
 				$permittedFields[] = 'password';
 				$permittedFields[] = 'language';
 				$permittedFields[] = 'locale';
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
+				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
+				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
 				$permittedFields[] = 'quota';
 			} else {
 				// No rights
@@ -532,7 +602,7 @@ class UsersController extends AUserData {
 		// Process the edit
 		switch ($key) {
 			case 'display':
-			case AccountManager::PROPERTY_DISPLAYNAME:
+			case IAccountManager::PROPERTY_DISPLAYNAME:
 				$targetUser->setDisplayName($value);
 				break;
 			case 'quota':
@@ -577,21 +647,25 @@ class UsersController extends AUserData {
 				}
 				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
 				break;
-			case AccountManager::PROPERTY_EMAIL:
+			case IAccountManager::PROPERTY_EMAIL:
 				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
 					$targetUser->setEMailAddress($value);
 				} else {
 					throw new OCSException('', 102);
 				}
 				break;
-			case AccountManager::PROPERTY_PHONE:
-			case AccountManager::PROPERTY_ADDRESS:
-			case AccountManager::PROPERTY_WEBSITE:
-			case AccountManager::PROPERTY_TWITTER:
+			case IAccountManager::PROPERTY_PHONE:
+			case IAccountManager::PROPERTY_ADDRESS:
+			case IAccountManager::PROPERTY_WEBSITE:
+			case IAccountManager::PROPERTY_TWITTER:
 				$userAccount = $this->accountManager->getUser($targetUser);
 				if ($userAccount[$key]['value'] !== $value) {
 					$userAccount[$key]['value'] = $value;
-					$this->accountManager->updateUser($targetUser, $userAccount);
+					try {
+						$this->accountManager->updateUser($targetUser, $userAccount, true);
+					} catch (\InvalidArgumentException $e) {
+						throw new OCSException('Invalid ' . $e->getMessage(), 102);
+					}
 				}
 				break;
 			default:
diff --git a/apps/provisioning_api/tests/Controller/GroupsControllerTest.php b/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
index 1973e6f8b6afca116b441ff63bffe7276b878029..bb8ec854390950792e168371db53807f5336c246 100644
--- a/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
+++ b/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
@@ -35,6 +35,7 @@ use OC\Group\Manager;
 use OC\SubAdmin;
 use OC\User\NoUserException;
 use OCA\Provisioning_API\Controller\GroupsController;
+use OCP\Accounts\IAccountManager;
 use OCP\IConfig;
 use OCP\ILogger;
 use OCP\IRequest;
@@ -185,10 +186,10 @@ class GroupsControllerTest extends \Test\TestCase {
 			->method('getUser')
 			->willReturnCallback(function (IUser $user) {
 				return [
-					AccountManager::PROPERTY_PHONE => ['value' => '0800-call-' . $user->getUID()],
-					AccountManager::PROPERTY_ADDRESS => ['value' => 'Holzweg 99, 0601 Herrera, Panama'],
-					AccountManager::PROPERTY_WEBSITE => ['value' => 'https://' . $user->getUid() . '.pa'],
-					AccountManager::PROPERTY_TWITTER => ['value' => '@' . $user->getUID()],
+					IAccountManager::PROPERTY_PHONE => ['value' => '0800-call-' . $user->getUID()],
+					IAccountManager::PROPERTY_ADDRESS => ['value' => 'Holzweg 99, 0601 Herrera, Panama'],
+					IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://' . $user->getUid() . '.pa'],
+					IAccountManager::PROPERTY_TWITTER => ['value' => '@' . $user->getUID()],
 				];
 			});
 	}
diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
index d508670d4f6ed7cb33331147e05625299b7dbfda..b6f28cc4a04c96861fffdc5c3007e2eb724005ee 100644
--- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php
+++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
@@ -48,6 +48,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider;
 use OCA\Provisioning_API\Controller\UsersController;
 use OCA\Provisioning_API\FederatedShareProviderFactory;
 use OCA\Settings\Mailer\NewUserMailHelper;
+use OCP\Accounts\IAccountManager;
 use OCP\App\IAppManager;
 use OCP\AppFramework\Http\DataResponse;
 use OCP\EventDispatcher\IEventDispatcher;
@@ -56,6 +57,7 @@ use OCP\IGroup;
 use OCP\IL10N;
 use OCP\ILogger;
 use OCP\IRequest;
+use OCP\IURLGenerator;
 use OCP\IUser;
 use OCP\IUserManager;
 use OCP\IUserSession;
@@ -85,6 +87,8 @@ class UsersControllerTest extends TestCase {
 	protected $api;
 	/** @var AccountManager|MockObject */
 	protected $accountManager;
+	/** @var IURLGenerator|MockObject */
+	protected $urlGenerator;
 	/** @var IRequest|MockObject */
 	protected $request;
 	/** @var IFactory|MockObject */
@@ -111,6 +115,7 @@ class UsersControllerTest extends TestCase {
 		$this->logger = $this->createMock(ILogger::class);
 		$this->request = $this->createMock(IRequest::class);
 		$this->accountManager = $this->createMock(AccountManager::class);
+		$this->urlGenerator = $this->createMock(IURLGenerator::class);
 		$this->l10nFactory = $this->createMock(IFactory::class);
 		$this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
 		$this->federatedShareProviderFactory = $this->createMock(FederatedShareProviderFactory::class);
@@ -128,6 +133,7 @@ class UsersControllerTest extends TestCase {
 				$this->groupManager,
 				$this->userSession,
 				$this->accountManager,
+				$this->urlGenerator,
 				$this->logger,
 				$this->l10nFactory,
 				$this->newUserMailHelper,
@@ -381,7 +387,7 @@ class UsersControllerTest extends TestCase {
 	}
 
 	public function testAddUserSuccessfulWithDisplayName() {
-		$api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+		$api = $this->getMockBuilder(UsersController::class)
 			->setConstructorArgs([
 				'provisioning_api',
 				$this->request,
@@ -391,6 +397,7 @@ class UsersControllerTest extends TestCase {
 				$this->groupManager,
 				$this->userSession,
 				$this->accountManager,
+				$this->urlGenerator,
 				$this->logger,
 				$this->l10nFactory,
 				$this->newUserMailHelper,
@@ -993,10 +1000,10 @@ class UsersControllerTest extends TestCase {
 			->with($targetUser)
 			->willReturn(
 				[
-					AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
-					AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
-					AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
-					AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+					IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+					IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+					IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+					IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
 				]
 			);
 		$this->config
@@ -1162,10 +1169,10 @@ class UsersControllerTest extends TestCase {
 			->with($targetUser)
 			->willReturn(
 				[
-					AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
-					AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
-					AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
-					AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+					IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+					IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+					IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+					IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
 				]
 			);
 
@@ -1333,10 +1340,10 @@ class UsersControllerTest extends TestCase {
 			->with($targetUser)
 			->willReturn(
 				[
-					AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
-					AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
-					AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
-					AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+					IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+					IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+					IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+					IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
 				]
 			);
 
@@ -1370,6 +1377,47 @@ class UsersControllerTest extends TestCase {
 		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
 	}
 
+	public function dataSearchByPhoneNumbers(): array {
+		return [
+			'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
+			'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
+			'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
+			'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
+			'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
+			'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
+			'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
+			'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
+		];
+	}
+
+	/**
+	 * @dataProvider dataSearchByPhoneNumbers
+	 * @param string $location
+	 * @param array $search
+	 * @param int $status
+	 * @param array $expected
+	 */
+	public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected) {
+		if ($searchUsers === null) {
+			$this->accountManager->expects($this->never())
+				->method('searchUsers');
+		} else {
+			$this->accountManager->expects($this->once())
+				->method('searchUsers')
+				->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
+				->willReturn($userMatches);
+		}
+
+		$this->urlGenerator->method('getAbsoluteURL')
+			->with('/')
+			->willReturn('https://localhost/');
+
+		$response = $this->api->searchByPhoneNumbers($location, $search);
+
+		self::assertEquals($status, $response->getStatus());
+		self::assertEquals($expected, $response->getData());
+	}
+
 	public function testEditUserRegularUserSelfEditChangeDisplayName() {
 		$loggedInUser = $this->getMockBuilder(IUser::class)
 			->disableOriginalConstructor()
@@ -3162,7 +3210,7 @@ class UsersControllerTest extends TestCase {
 			->willReturn($user);
 
 		/** @var UsersController | MockObject $api */
-		$api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+		$api = $this->getMockBuilder(UsersController::class)
 			->setConstructorArgs([
 				'provisioning_api',
 				$this->request,
@@ -3172,6 +3220,7 @@ class UsersControllerTest extends TestCase {
 				$this->groupManager,
 				$this->userSession,
 				$this->accountManager,
+				$this->urlGenerator,
 				$this->logger,
 				$this->l10nFactory,
 				$this->newUserMailHelper,
@@ -3227,7 +3276,7 @@ class UsersControllerTest extends TestCase {
 
 	public function testGetUser() {
 		/** @var UsersController | MockObject $api */
-		$api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+		$api = $this->getMockBuilder(UsersController::class)
 			->setConstructorArgs([
 				'provisioning_api',
 				$this->request,
@@ -3237,6 +3286,7 @@ class UsersControllerTest extends TestCase {
 				$this->groupManager,
 				$this->userSession,
 				$this->accountManager,
+				$this->urlGenerator,
 				$this->logger,
 				$this->l10nFactory,
 				$this->newUserMailHelper,
@@ -3566,22 +3616,22 @@ class UsersControllerTest extends TestCase {
 		return [
 			[false, false, []],
 			[false,  true, [
-				AccountManager::PROPERTY_PHONE,
-				AccountManager::PROPERTY_ADDRESS,
-				AccountManager::PROPERTY_WEBSITE,
-				AccountManager::PROPERTY_TWITTER,
+				IAccountManager::PROPERTY_PHONE,
+				IAccountManager::PROPERTY_ADDRESS,
+				IAccountManager::PROPERTY_WEBSITE,
+				IAccountManager::PROPERTY_TWITTER,
 			]],
 			[ true, false, [
-				AccountManager::PROPERTY_DISPLAYNAME,
-				AccountManager::PROPERTY_EMAIL,
+				IAccountManager::PROPERTY_DISPLAYNAME,
+				IAccountManager::PROPERTY_EMAIL,
 			]],
 			[ true,  true ,[
-				AccountManager::PROPERTY_DISPLAYNAME,
-				AccountManager::PROPERTY_EMAIL,
-				AccountManager::PROPERTY_PHONE,
-				AccountManager::PROPERTY_ADDRESS,
-				AccountManager::PROPERTY_WEBSITE,
-				AccountManager::PROPERTY_TWITTER,
+				IAccountManager::PROPERTY_DISPLAYNAME,
+				IAccountManager::PROPERTY_EMAIL,
+				IAccountManager::PROPERTY_PHONE,
+				IAccountManager::PROPERTY_ADDRESS,
+				IAccountManager::PROPERTY_WEBSITE,
+				IAccountManager::PROPERTY_TWITTER,
 			]]
 		];
 	}
diff --git a/apps/settings/js/usersettings.js b/apps/settings/js/usersettings.js
index fcfe556b1d9a67183ecadf6a1a7982af995753ee..a02aae6fb6ac4987faa3d07e50599fe854ae4060 100644
--- a/apps/settings/js/usersettings.js
+++ b/apps/settings/js/usersettings.js
@@ -24,6 +24,11 @@
 			if (_.isUndefined(data)) {
 				return null;
 			}
+
+			if (data.status && data.status === 'error') {
+				OC.Notification.show(data.data.message, { type: 'error' });
+			}
+
 			if (_.isUndefined(data.data)) {
 				return null;
 			}
@@ -47,4 +52,4 @@
 	OC.Settings = OC.Settings || {};
 
 	OC.Settings.UserSettings = UserSettings;
-})();
\ No newline at end of file
+})();
diff --git a/apps/settings/lib/BackgroundJobs/VerifyUserData.php b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
index 0faa9b56e8294e126d1b79dd14f8dd3eb6c1a864..d1b6d835fa4b790852d5312b475b73666cdfaec4 100644
--- a/apps/settings/lib/BackgroundJobs/VerifyUserData.php
+++ b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
@@ -30,6 +30,7 @@
 namespace OCA\Settings\BackgroundJobs;
 
 use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
 use OCP\AppFramework\Http;
 use OCP\BackgroundJob\IJobList;
 use OCP\BackgroundJob\Job;
@@ -114,11 +115,11 @@ class VerifyUserData extends Job {
 		$try = (int)$argument['try'] + 1;
 
 		switch ($argument['type']) {
-			case AccountManager::PROPERTY_WEBSITE:
+			case IAccountManager::PROPERTY_WEBSITE:
 				$result = $this->verifyWebsite($argument);
 				break;
-			case AccountManager::PROPERTY_TWITTER:
-			case AccountManager::PROPERTY_EMAIL:
+			case IAccountManager::PROPERTY_TWITTER:
+			case IAccountManager::PROPERTY_EMAIL:
 				$result = $this->verifyViaLookupServer($argument, $argument['type']);
 				break;
 			default:
@@ -164,9 +165,9 @@ class VerifyUserData extends Job {
 			$userData = $this->accountManager->getUser($user);
 
 			if ($publishedCodeSanitized === $argument['verificationCode']) {
-				$userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
+				$userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
 			} else {
-				$userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
+				$userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
 			}
 
 			$this->accountManager->updateUser($user, $userData);
diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php
index 7929e9c3962089525782c97b10fa533a33264c24..1ebeb41adfbfc5bb029ca21b007cdde36f6d93d0 100644
--- a/apps/settings/lib/Controller/CheckSetupController.php
+++ b/apps/settings/lib/Controller/CheckSetupController.php
@@ -752,6 +752,7 @@ Raw output
 				PhpOutputBuffering::class => ['pass' => $phpOutputBuffering->run(), 'description' => $phpOutputBuffering->description(), 'severity' => $phpOutputBuffering->severity()],
 				LegacySSEKeyFormat::class => ['pass' => $legacySSEKeyFormat->run(), 'description' => $legacySSEKeyFormat->description(), 'severity' => $legacySSEKeyFormat->severity(), 'linkToDocumentation' => $legacySSEKeyFormat->linkToDocumentation()],
 				CheckUserCertificates::class => ['pass' => $checkUserCertificates->run(), 'description' => $checkUserCertificates->description(), 'severity' => $checkUserCertificates->severity(), 'elements' => $checkUserCertificates->elements()],
+				'isDefaultPhoneRegionSet' => $this->config->getSystemValueString('default_phone_region', '') !== '',
 			]
 		);
 	}
diff --git a/apps/settings/lib/Controller/UsersController.php b/apps/settings/lib/Controller/UsersController.php
index cad21c5f3b34f1256206f5f276688e1347ba7250..dba5ec69b2b8fa53fe51cd72906c11a3ca7de6ca 100644
--- a/apps/settings/lib/Controller/UsersController.php
+++ b/apps/settings/lib/Controller/UsersController.php
@@ -1,4 +1,6 @@
 <?php
+
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2016, ownCloud, Inc.
  *
@@ -30,7 +32,6 @@
 
 // FIXME: disabled for now to be able to inject IGroupManager and also use
 // getSubAdmin()
-//declare(strict_types=1);
 
 namespace OCA\Settings\Controller;
 
@@ -46,6 +47,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider;
 use OCA\Settings\BackgroundJobs\VerifyUserData;
 use OCA\Settings\Events\BeforeTemplateRenderedEvent;
 use OCA\User_LDAP\User_Proxy;
+use OCP\Accounts\IAccountManager;
 use OCP\App\IAppManager;
 use OCP\AppFramework\Controller;
 use OCP\AppFramework\Http\DataResponse;
@@ -140,7 +142,7 @@ class UsersController extends Controller {
 	 *
 	 * @return TemplateResponse
 	 */
-	public function usersListByGroup() {
+	public function usersListByGroup(): TemplateResponse {
 		return $this->usersList();
 	}
 
@@ -152,7 +154,7 @@ class UsersController extends Controller {
 	 *
 	 * @return TemplateResponse
 	 */
-	public function usersList() {
+	public function usersList(): TemplateResponse {
 		$user = $this->userSession->getUser();
 		$uid = $user->getUID();
 
@@ -309,7 +311,7 @@ class UsersController extends Controller {
 	 *
 	 * @return bool
 	 */
-	protected function canAdminChangeUserPasswords() {
+	protected function canAdminChangeUserPasswords(): bool {
 		$isEncryptionEnabled = $this->encryptionManager->isEnabled();
 		try {
 			$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
@@ -344,19 +346,19 @@ class UsersController extends Controller {
 	 * @param string $twitterScope
 	 * @return DataResponse
 	 */
-	public function setUserSettings($avatarScope,
-									$displayname,
-									$displaynameScope,
-									$phone,
-									$phoneScope,
-									$email,
-									$emailScope,
-									$website,
-									$websiteScope,
-									$address,
-									$addressScope,
-									$twitter,
-									$twitterScope
+	public function setUserSettings(string $avatarScope,
+									string $displayname,
+									string $displaynameScope,
+									string $phone,
+									string $phoneScope,
+									string $email,
+									string $emailScope,
+									string $website,
+									string $websiteScope,
+									string $address,
+									string $addressScope,
+									string $twitter,
+									string $twitterScope
 	) {
 		$email = strtolower($email);
 		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
@@ -372,36 +374,40 @@ class UsersController extends Controller {
 		}
 		$user = $this->userSession->getUser();
 		$data = $this->accountManager->getUser($user);
-		$data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
+		$data[IAccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
 		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
-			$data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
-			$data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
+			$data[IAccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
+			$data[IAccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
 		}
 		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
 			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
 			if ($shareProvider->isLookupServerUploadEnabled()) {
-				$data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
-				$data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
-				$data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
-				$data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
+				$data[IAccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
+				$data[IAccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
+				$data[IAccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
+				$data[IAccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
 			}
 		}
 		try {
-			$this->saveUserSettings($user, $data);
+			$data = $this->saveUserSettings($user, $data);
 			return new DataResponse(
 				[
 					'status' => 'success',
 					'data' => [
 						'userId' => $user->getUID(),
-						'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
-						'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
-						'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
-						'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
-						'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
-						'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
-						'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
-						'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
-						'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
+						'avatarScope' => $data[IAccountManager::PROPERTY_AVATAR]['scope'],
+						'displayname' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
+						'displaynameScope' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
+						'phone' => $data[IAccountManager::PROPERTY_PHONE]['value'],
+						'phoneScope' => $data[IAccountManager::PROPERTY_PHONE]['scope'],
+						'email' => $data[IAccountManager::PROPERTY_EMAIL]['value'],
+						'emailScope' => $data[IAccountManager::PROPERTY_EMAIL]['scope'],
+						'website' => $data[IAccountManager::PROPERTY_WEBSITE]['value'],
+						'websiteScope' => $data[IAccountManager::PROPERTY_WEBSITE]['scope'],
+						'address' => $data[IAccountManager::PROPERTY_ADDRESS]['value'],
+						'addressScope' => $data[IAccountManager::PROPERTY_ADDRESS]['scope'],
+						'twitter' => $data[IAccountManager::PROPERTY_TWITTER]['value'],
+						'twitterScope' => $data[IAccountManager::PROPERTY_TWITTER]['scope'],
 						'message' => $this->l10n->t('Settings saved')
 					]
 				],
@@ -414,6 +420,13 @@ class UsersController extends Controller {
 					'message' => $e->getMessage()
 				],
 			]);
+		} catch (\InvalidArgumentException $e) {
+			return new DataResponse([
+				'status' => 'error',
+				'data' => [
+					'message' => $e->getMessage()
+				],
+			]);
 		}
 	}
 	/**
@@ -421,34 +434,45 @@ class UsersController extends Controller {
 	 *
 	 * @param IUser $user
 	 * @param array $data
+	 * @return array
 	 * @throws ForbiddenException
+	 * @throws \InvalidArgumentException
 	 */
-	protected function saveUserSettings(IUser $user, array $data) {
+	protected function saveUserSettings(IUser $user, array $data): array {
 		// keep the user back-end up-to-date with the latest display name and email
 		// address
 		$oldDisplayName = $user->getDisplayName();
 		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
+		if (isset($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
+			&& $oldDisplayName !== $data[IAccountManager::PROPERTY_DISPLAYNAME]['value']
 		) {
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
+			$result = $user->setDisplayName($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
 			if ($result === false) {
 				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
 			}
 		}
+
 		$oldEmailAddress = $user->getEMailAddress();
 		$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
+		if (isset($data[IAccountManager::PROPERTY_EMAIL]['value'])
+			&& $oldEmailAddress !== $data[IAccountManager::PROPERTY_EMAIL]['value']
 		) {
 			// this is the only permission a backend provides and is also used
 			// for the permission of setting a email address
 			if (!$user->canChangeDisplayName()) {
 				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
 			}
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
+			$user->setEMailAddress($data[IAccountManager::PROPERTY_EMAIL]['value']);
+		}
+
+		try {
+			return $this->accountManager->updateUser($user, $data, true);
+		} catch (\InvalidArgumentException $e) {
+			if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
+				throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
+			}
+			throw new \InvalidArgumentException($this->l10n->t('Some account data was invalid'));
 		}
-		$this->accountManager->updateUser($user, $data);
 	}
 
 	/**
@@ -479,26 +503,25 @@ class UsersController extends Controller {
 
 		switch ($account) {
 			case 'verify-twitter':
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+				$accountData[IAccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
 				$msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
 				$code = $codeMd5;
-				$type = AccountManager::PROPERTY_TWITTER;
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
-				$accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
+				$type = IAccountManager::PROPERTY_TWITTER;
+				$accountData[IAccountManager::PROPERTY_TWITTER]['signature'] = $signature;
 				break;
 			case 'verify-website':
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+				$accountData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
 				$msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
-				$type = AccountManager::PROPERTY_WEBSITE;
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
-				$accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
+				$type = IAccountManager::PROPERTY_WEBSITE;
+				$accountData[IAccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
 				break;
 			default:
 				return new DataResponse([], Http::STATUS_BAD_REQUEST);
 		}
 
 		if ($onlyVerificationCode === false) {
-			$this->accountManager->updateUser($user, $accountData);
+			$accountData = $this->accountManager->updateUser($user, $accountData);
+			$data = $accountData[$type]['value'];
 
 			$this->jobList->add(VerifyUserData::class,
 				[
diff --git a/apps/settings/lib/Settings/Personal/PersonalInfo.php b/apps/settings/lib/Settings/Personal/PersonalInfo.php
index 06ea440afabe127627e647b7f3504b99e875af95..d9f9c2b3a7d0492fb08d76c0d660719c4b2f9450 100644
--- a/apps/settings/lib/Settings/Personal/PersonalInfo.php
+++ b/apps/settings/lib/Settings/Personal/PersonalInfo.php
@@ -1,4 +1,6 @@
 <?php
+
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
  *
@@ -33,6 +35,7 @@ namespace OCA\Settings\Settings\Personal;
 
 use OC\Accounts\AccountManager;
 use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCP\Accounts\IAccountManager;
 use OCP\App\IAppManager;
 use OCP\AppFramework\Http\TemplateResponse;
 use OCP\Files\FileInfo;
@@ -62,14 +65,6 @@ class PersonalInfo implements ISettings {
 	/** @var IL10N */
 	private $l;
 
-	/**
-	 * @param IConfig $config
-	 * @param IUserManager $userManager
-	 * @param IGroupManager $groupManager
-	 * @param AccountManager $accountManager
-	 * @param IFactory $l10nFactory
-	 * @param IL10N $l
-	 */
 	public function __construct(
 		IConfig $config,
 		IUserManager $userManager,
@@ -88,11 +83,7 @@ class PersonalInfo implements ISettings {
 		$this->l = $l;
 	}
 
-	/**
-	 * @return TemplateResponse returns the instance with all parameters set, ready to be rendered
-	 * @since 9.1
-	 */
-	public function getForm() {
+	public function getForm(): TemplateResponse {
 		$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
 		$lookupServerUploadEnabled = false;
 		if ($federatedFileSharingEnabled) {
@@ -126,23 +117,23 @@ class PersonalInfo implements ISettings {
 			'quota' => $storageInfo['quota'],
 			'avatarChangeSupported' => $user->canChangeAvatar(),
 			'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
-			'avatarScope' => $userData[AccountManager::PROPERTY_AVATAR]['scope'],
+			'avatarScope' => $userData[IAccountManager::PROPERTY_AVATAR]['scope'],
 			'displayNameChangeSupported' => $user->canChangeDisplayName(),
-			'displayName' => $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'],
-			'displayNameScope' => $userData[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
-			'email' => $userData[AccountManager::PROPERTY_EMAIL]['value'],
-			'emailScope' => $userData[AccountManager::PROPERTY_EMAIL]['scope'],
-			'emailVerification' => $userData[AccountManager::PROPERTY_EMAIL]['verified'],
-			'phone' => $userData[AccountManager::PROPERTY_PHONE]['value'],
-			'phoneScope' => $userData[AccountManager::PROPERTY_PHONE]['scope'],
-			'address' => $userData[AccountManager::PROPERTY_ADDRESS]['value'],
-			'addressScope' => $userData[AccountManager::PROPERTY_ADDRESS]['scope'],
-			'website' => $userData[AccountManager::PROPERTY_WEBSITE]['value'],
-			'websiteScope' => $userData[AccountManager::PROPERTY_WEBSITE]['scope'],
-			'websiteVerification' => $userData[AccountManager::PROPERTY_WEBSITE]['verified'],
-			'twitter' => $userData[AccountManager::PROPERTY_TWITTER]['value'],
-			'twitterScope' => $userData[AccountManager::PROPERTY_TWITTER]['scope'],
-			'twitterVerification' => $userData[AccountManager::PROPERTY_TWITTER]['verified'],
+			'displayName' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
+			'displayNameScope' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
+			'email' => $userData[IAccountManager::PROPERTY_EMAIL]['value'],
+			'emailScope' => $userData[IAccountManager::PROPERTY_EMAIL]['scope'],
+			'emailVerification' => $userData[IAccountManager::PROPERTY_EMAIL]['verified'],
+			'phone' => $userData[IAccountManager::PROPERTY_PHONE]['value'],
+			'phoneScope' => $userData[IAccountManager::PROPERTY_PHONE]['scope'],
+			'address' => $userData[IAccountManager::PROPERTY_ADDRESS]['value'],
+			'addressScope' => $userData[IAccountManager::PROPERTY_ADDRESS]['scope'],
+			'website' => $userData[IAccountManager::PROPERTY_WEBSITE]['value'],
+			'websiteScope' => $userData[IAccountManager::PROPERTY_WEBSITE]['scope'],
+			'websiteVerification' => $userData[IAccountManager::PROPERTY_WEBSITE]['verified'],
+			'twitter' => $userData[IAccountManager::PROPERTY_TWITTER]['value'],
+			'twitterScope' => $userData[IAccountManager::PROPERTY_TWITTER]['scope'],
+			'twitterVerification' => $userData[IAccountManager::PROPERTY_TWITTER]['verified'],
 			'groups' => $this->getGroups($user),
 		] + $messageParameters + $languageParameters + $localeParameters;
 
@@ -154,7 +145,7 @@ class PersonalInfo implements ISettings {
 	 * @return string the section ID, e.g. 'sharing'
 	 * @since 9.1
 	 */
-	public function getSection() {
+	public function getSection(): string {
 		return 'personal-info';
 	}
 
@@ -166,7 +157,7 @@ class PersonalInfo implements ISettings {
 	 * E.g.: 70
 	 * @since 9.1
 	 */
-	public function getPriority() {
+	public function getPriority(): int {
 		return 10;
 	}
 
@@ -176,9 +167,9 @@ class PersonalInfo implements ISettings {
 	 * @param IUser $user
 	 * @return array
 	 */
-	private function getGroups(IUser $user) {
+	private function getGroups(IUser $user): array {
 		$groups = array_map(
-			function (IGroup $group) {
+			static function (IGroup $group) {
 				return $group->getDisplayName();
 			},
 			$this->groupManager->getUserGroups($user)
@@ -195,7 +186,7 @@ class PersonalInfo implements ISettings {
 	 * @param IUser $user
 	 * @return array
 	 */
-	private function getLanguages(IUser $user) {
+	private function getLanguages(IUser $user): array {
 		$forceLanguage = $this->config->getSystemValue('force_language', false);
 		if ($forceLanguage !== false) {
 			return [];
@@ -228,7 +219,7 @@ class PersonalInfo implements ISettings {
 		);
 	}
 
-	private function getLocales(IUser $user) {
+	private function getLocales(IUser $user): array {
 		$forceLanguage = $this->config->getSystemValue('force_locale', false);
 		if ($forceLanguage !== false) {
 			return [];
@@ -273,8 +264,8 @@ class PersonalInfo implements ISettings {
 	 * @param array $userData
 	 * @return array
 	 */
-	private function getMessageParameters(array $userData) {
-		$needVerifyMessage = [AccountManager::PROPERTY_EMAIL, AccountManager::PROPERTY_WEBSITE, AccountManager::PROPERTY_TWITTER];
+	private function getMessageParameters(array $userData): array {
+		$needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
 		$messageParameters = [];
 		foreach ($needVerifyMessage as $property) {
 			switch ($userData[$property]['verified']) {
diff --git a/apps/settings/templates/settings/personal/personal.info.php b/apps/settings/templates/settings/personal/personal.info.php
index b78c162c6c9e0f75d062dc2fffe804b41732630c..84198b3c0c43e6d3c9b6e88b9d6fb35d917e3048 100644
--- a/apps/settings/templates/settings/personal/personal.info.php
+++ b/apps/settings/templates/settings/personal/personal.info.php
@@ -67,6 +67,7 @@ script('settings', [
 					</div>
 				</div>
 				<span class="icon-checkmark hidden"></span>
+				<span class="icon-error hidden" ></span>
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
 				<input type="hidden" id="avatarscope" value="<?php p($_['avatarScope']) ?>">
 				<?php } ?>
@@ -161,7 +162,7 @@ script('settings', [
 					} ?>
 					   placeholder="<?php p($l->t('Your email address')); ?>"
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
-			   	<span class="icon-checkmark hidden"></span>
+				<span class="icon-checkmark hidden"></span>
 				<span class="icon-error hidden" ></span>
 				<?php if (!$_['displayNameChangeSupported']) { ?>
 					<span><?php if (isset($_['email']) && !empty($_['email'])) {
@@ -196,6 +197,7 @@ script('settings', [
 					   placeholder="<?php p($l->t('Your phone number')); ?>"
 				       autocomplete="on" autocapitalize="none" autocorrect="off" />
 				<span class="icon-checkmark hidden"></span>
+				<span class="icon-error hidden" ></span>
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
 				<input type="hidden" id="phonescope" value="<?php p($_['phoneScope']) ?>">
 				<?php } ?>
@@ -220,6 +222,7 @@ script('settings', [
 					   value="<?php p($_['address']) ?>"
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
 				<span class="icon-checkmark hidden"></span>
+				<span class="icon-error hidden" ></span>
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
 				<input type="hidden" id="addressscope" value="<?php p($_['addressScope']) ?>">
 				<?php } ?>
@@ -275,6 +278,7 @@ script('settings', [
 					}  ?>
 				/>
 				<span class="icon-checkmark hidden"></span>
+				<span class="icon-error hidden" ></span>
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
 				<input type="hidden" id="websitescope" value="<?php p($_['websiteScope']) ?>">
 				<?php } ?>
@@ -330,6 +334,7 @@ script('settings', [
 					}  ?>
 				/>
 				<span class="icon-checkmark hidden"></span>
+				<span class="icon-error hidden" ></span>
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
 				<input type="hidden" id="twitterscope" value="<?php p($_['twitterScope']) ?>">
 				<?php } ?>
diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php
index 43ec984041c8a96ab861711e18cd39351b95e6b1..965d75863438239077918dc70d10451e181f8ae0 100644
--- a/apps/settings/tests/Controller/CheckSetupControllerTest.php
+++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php
@@ -605,6 +605,7 @@ class CheckSetupControllerTest extends TestCase {
 				'OCA\Settings\SetupChecks\LegacySSEKeyFormat' => ['pass' => true, 'description' => 'The old server-side-encryption format is enabled. We recommend disabling this.', 'severity' => 'warning', 'linkToDocumentation' => ''],
 				'OCA\Settings\SetupChecks\CheckUserCertificates' => ['pass' => false, 'description' => 'There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via "occ security:certificates:import" command. Their paths inside the data directory are shown below.', 'severity' => 'warning', 'elements' => ['a', 'b']],
 				'imageMagickLacksSVGSupport' => false,
+				'isDefaultPhoneRegionSet' => false,
 			]
 		);
 		$this->assertEquals($expected, $this->checkSetupController->check());
diff --git a/apps/settings/tests/Controller/UsersControllerTest.php b/apps/settings/tests/Controller/UsersControllerTest.php
index 4679fd8f7ba0f664183c2aad1e65bd09e9df87dc..23e3ef5ec01178f030b02523a9cadd5b6d507805 100644
--- a/apps/settings/tests/Controller/UsersControllerTest.php
+++ b/apps/settings/tests/Controller/UsersControllerTest.php
@@ -31,6 +31,7 @@ use OC\Accounts\AccountManager;
 use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
 use OC\Group\Manager;
 use OCA\Settings\Controller\UsersController;
+use OCP\Accounts\IAccountManager;
 use OCP\App\IAppManager;
 use OCP\AppFramework\Http;
 use OCP\BackgroundJob\IJobList;
@@ -196,41 +197,41 @@ class UsersControllerTest extends \Test\TestCase {
 				->method('getUser')
 				->with($user)
 				->willReturn([
-					AccountManager::PROPERTY_DISPLAYNAME =>
+					IAccountManager::PROPERTY_DISPLAYNAME =>
 						[
 							'value' => 'Display name',
 							'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 							'verified' => AccountManager::NOT_VERIFIED,
 						],
-					AccountManager::PROPERTY_ADDRESS =>
+					IAccountManager::PROPERTY_ADDRESS =>
 						[
 							'value' => '',
 							'scope' => AccountManager::VISIBILITY_PRIVATE,
 							'verified' => AccountManager::NOT_VERIFIED,
 						],
-					AccountManager::PROPERTY_WEBSITE =>
+					IAccountManager::PROPERTY_WEBSITE =>
 						[
 							'value' => '',
 							'scope' => AccountManager::VISIBILITY_PRIVATE,
 							'verified' => AccountManager::NOT_VERIFIED,
 						],
-					AccountManager::PROPERTY_EMAIL =>
+					IAccountManager::PROPERTY_EMAIL =>
 						[
 							'value' => '',
 							'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
 							'verified' => AccountManager::NOT_VERIFIED,
 						],
-					AccountManager::PROPERTY_AVATAR =>
+					IAccountManager::PROPERTY_AVATAR =>
 						[
 							'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
 						],
-					AccountManager::PROPERTY_PHONE =>
+					IAccountManager::PROPERTY_PHONE =>
 						[
 							'value' => '',
 							'scope' => AccountManager::VISIBILITY_PRIVATE,
 							'verified' => AccountManager::NOT_VERIFIED,
 						],
-					AccountManager::PROPERTY_TWITTER =>
+					IAccountManager::PROPERTY_TWITTER =>
 						[
 							'value' => '',
 							'scope' => AccountManager::VISIBILITY_PRIVATE,
@@ -238,12 +239,14 @@ class UsersControllerTest extends \Test\TestCase {
 						],
 				]);
 
-			$controller->expects($this->once())->method('saveUserSettings');
+			$controller->expects($this->once())
+				->method('saveUserSettings')
+				->willReturnArgument(1);
 		} else {
 			$controller->expects($this->never())->method('saveUserSettings');
 		}
 
-		$result = $controller->setUserSettings(
+		$result = $controller->setUserSettings(//
 			AccountManager::VISIBILITY_CONTACTS_ONLY,
 			'displayName',
 			AccountManager::VISIBILITY_CONTACTS_ONLY,
@@ -289,21 +292,21 @@ class UsersControllerTest extends \Test\TestCase {
 		$user->method('getEMailAddress')->willReturn($oldEmailAddress);
 		$user->method('canChangeDisplayName')->willReturn(true);
 
-		if ($data[AccountManager::PROPERTY_EMAIL]['value'] === $oldEmailAddress ||
-			($oldEmailAddress === null && $data[AccountManager::PROPERTY_EMAIL]['value'] === '')) {
+		if ($data[IAccountManager::PROPERTY_EMAIL]['value'] === $oldEmailAddress ||
+			($oldEmailAddress === null && $data[IAccountManager::PROPERTY_EMAIL]['value'] === '')) {
 			$user->expects($this->never())->method('setEMailAddress');
 		} else {
 			$user->expects($this->once())->method('setEMailAddress')
-				->with($data[AccountManager::PROPERTY_EMAIL]['value'])
+				->with($data[IAccountManager::PROPERTY_EMAIL]['value'])
 				->willReturn(true);
 		}
 
-		if ($data[AccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ||
-			($oldDisplayName === null && $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] === '')) {
+		if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ||
+			($oldDisplayName === null && $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === '')) {
 			$user->expects($this->never())->method('setDisplayName');
 		} else {
 			$user->expects($this->once())->method('setDisplayName')
-				->with($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
+				->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
 				->willReturn(true);
 		}
 
@@ -317,48 +320,48 @@ class UsersControllerTest extends \Test\TestCase {
 		return [
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'john@example.com',
 				'john doe'
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'johnNew@example.com',
 				'john New doe'
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'johnNew@example.com',
 				'john doe'
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'john@example.com',
 				'john New doe'
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => ''],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				null,
 				'john New doe'
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'john@example.com',
 				null
@@ -391,14 +394,14 @@ class UsersControllerTest extends \Test\TestCase {
 		$user->method('getDisplayName')->willReturn($oldDisplayName);
 		$user->method('getEMailAddress')->willReturn($oldEmailAddress);
 
-		if ($data[AccountManager::PROPERTY_EMAIL]['value'] !== $oldEmailAddress) {
+		if ($data[IAccountManager::PROPERTY_EMAIL]['value'] !== $oldEmailAddress) {
 			$user->method('canChangeDisplayName')
 				->willReturn($canChangeEmail);
 		}
 
-		if ($data[AccountManager::PROPERTY_DISPLAYNAME]['value'] !== $oldDisplayName) {
+		if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $oldDisplayName) {
 			$user->method('setDisplayName')
-				->with($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
+				->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
 				->willReturn($setDisplayNameResult);
 		}
 
@@ -410,8 +413,8 @@ class UsersControllerTest extends \Test\TestCase {
 		return [
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'johnNew@example.com',
 				'john New doe',
@@ -420,8 +423,8 @@ class UsersControllerTest extends \Test\TestCase {
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'johnNew@example.com',
 				'john New doe',
@@ -430,8 +433,8 @@ class UsersControllerTest extends \Test\TestCase {
 			],
 			[
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
 				],
 				'johnNew@example.com',
 				'john New doe',
@@ -455,7 +458,7 @@ class UsersControllerTest extends \Test\TestCase {
 		$signature = 'theSignature';
 
 		$code = $message . ' ' . $signature;
-		if ($type === AccountManager::PROPERTY_TWITTER) {
+		if ($type === IAccountManager::PROPERTY_TWITTER) {
 			$code = $message . ' ' . md5($signature);
 		}
 
@@ -470,7 +473,7 @@ class UsersControllerTest extends \Test\TestCase {
 		$controller->expects($this->any())->method('getCurrentTime')->willReturn(1234567);
 
 		if ($onlyVerificationCode === false) {
-			$this->accountManager->expects($this->once())->method('updateUser')->with($user, $expectedData);
+			$this->accountManager->expects($this->once())->method('updateUser')->with($user, $expectedData)->willReturnArgument(1);
 			$this->jobList->expects($this->once())->method('add')
 				->with('OCA\Settings\BackgroundJobs\VerifyUserData',
 					[
@@ -492,25 +495,25 @@ class UsersControllerTest extends \Test\TestCase {
 
 	public function dataTestGetVerificationCode() {
 		$accountDataBefore = [
-			AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
-			AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
+			IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
+			IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
 		];
 
 		$accountDataAfterWebsite = [
-			AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
-			AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
+			IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
+			IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
 		];
 
 		$accountDataAfterTwitter = [
-			AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
-			AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
+			IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
+			IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
 		];
 
 		return [
-			['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false],
-			['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false],
-			['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true],
-			['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true],
+			['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false],
+			['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false],
+			['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true],
+			['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true],
 		];
 	}
 
diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php
index 56d75c058aadf5e69195ff03611520ce81ac78ba..a856e47ef2b2afe5aa4584df9bc45a6f4f4fcefa 100644
--- a/build/integration/features/bootstrap/Provisioning.php
+++ b/build/integration/features/bootstrap/Provisioning.php
@@ -173,6 +173,37 @@ trait Provisioning {
 		}
 	}
 
+	/**
+	 * @Then /^search users by phone for region "([^"]*)" with$/
+	 *
+	 * @param string $user
+	 * @param \Behat\Gherkin\Node\TableNode|null $settings
+	 */
+	public function searchUserByPhone($region, \Behat\Gherkin\Node\TableNode $searchTable) {
+		$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/search/by-phone";
+		$client = new Client();
+		$options = [];
+		$options['auth'] = $this->adminUser;
+		$options['headers'] = [
+			'OCS-APIREQUEST' => 'true',
+		];
+
+		$search = [];
+		foreach ($searchTable->getRows() as $row) {
+			if (!isset($search[$row[0]])) {
+				$search[$row[0]] = [];
+			}
+			$search[$row[0]][] = $row[1];
+		}
+
+		$options['form_params'] = [
+			'location' => $region,
+			'search' => $search,
+		];
+
+		$this->response = $client->post($fullUrl, $options);
+	}
+
 	public function createUser($user) {
 		$previous_user = $this->currentUser;
 		$this->currentUser = "admin";
@@ -560,6 +591,19 @@ trait Provisioning {
 		}
 	}
 
+	/**
+	 * @Then /^phone matches returned are$/
+	 * @param \Behat\Gherkin\Node\TableNode|null $usersList
+	 */
+	public function thePhoneUsersShouldBe($usersList) {
+		if ($usersList instanceof \Behat\Gherkin\Node\TableNode) {
+			$users = $usersList->getRowsHash();
+			$listCheckedElements = simplexml_load_string($this->response->getBody())->data;
+			$respondedArray = json_decode(json_encode($listCheckedElements), true);
+			Assert::assertEquals($users, $respondedArray);
+		}
+	}
+
 	/**
 	 * @Then /^detailed users returned are$/
 	 * @param \Behat\Gherkin\Node\TableNode|null $usersList
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index ecc33c657f41af0eeb98c610b3fd9320ddf838ab..717aa04e4bdbc6c06c56caac6ce11cf40a0376ee 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -71,12 +71,12 @@ Feature: provisioning
 		And the HTTP status code should be "200"
 		And sending "PUT" to "/cloud/users/brand-new-user" with
 			| key | email |
-			| value | brand-new-user@gmail.com |
+			| value | no-reply@nextcloud.com |
 		And the OCS status code should be "100"
 		And the HTTP status code should be "200"
 		And sending "PUT" to "/cloud/users/brand-new-user" with
 			| key | phone |
-			| value | 0123 456 789 |
+			| value | +49 711 / 25 24 28-90 |
 		And the OCS status code should be "100"
 		And the HTTP status code should be "200"
 		And sending "PUT" to "/cloud/users/brand-new-user" with
@@ -97,12 +97,29 @@ Feature: provisioning
 		Then user "brand-new-user" has
 			| id | brand-new-user |
 			| displayname | Brand New User |
-			| email | brand-new-user@gmail.com |
-			| phone | 0123 456 789 |
+			| email | no-reply@nextcloud.com |
+			| phone | +4971125242890 |
 			| address | Foo Bar Town |
 			| website | https://nextcloud.com |
 			| twitter | Nextcloud |
 
+	Scenario: Search by phone number
+		Given As an "admin"
+		And user "phone-user" exists
+		And sending "PUT" to "/cloud/users/phone-user" with
+			| key | phone |
+			| value | +49 711 / 25 24 28-90 |
+		And the OCS status code should be "100"
+		And the HTTP status code should be "200"
+		Then search users by phone for region "DE" with
+			| random-string1 | 0711 / 123 456 78 |
+			| random-string1 | 0711 / 252 428-90 |
+			| random-string2 | 0711 / 90-824 252 |
+		And the OCS status code should be "100"
+		And the HTTP status code should be "200"
+		Then phone matches returned are
+			| random-string1 | phone-user@localhost:8080 |
+
 	Scenario: Create a group
 		Given As an "admin"
 		And group "new-group" does not exist
diff --git a/config/config.sample.php b/config/config.sample.php
index 2710fbf5fdb5b6018bada6dc3e5a871563558fb3..8adb5bf2168f8f59c36322d531f511d9f018ace5 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -194,6 +194,16 @@ $CONFIG = [
  */
 'default_locale' => 'en_US',
 
+/**
+ * This sets the default region for phone numbers on your Nextcloud server,
+ * using ISO 3166-1 country codes such as ``DE`` for Germany, ``FR`` for France, …
+ * It is required to allow inserting phone numbers in the user profiles starting
+ * without the country code (e.g. +49 for Germany).
+ *
+ * No default value!
+ */
+'default_phone_region' => 'EN',
+
 /**
  * With this setting a locale can be forced for all users. If a locale is
  * forced, the users are also unable to change their locale in the personal
diff --git a/core/Migrations/Version21000Date20201202095923.php b/core/Migrations/Version21000Date20201202095923.php
new file mode 100644
index 0000000000000000000000000000000000000000..6433d8c9b7a4f121942d06c3cde688dfed747ce1
--- /dev/null
+++ b/core/Migrations/Version21000Date20201202095923.php
@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Core\Migrations;
+
+use Closure;
+use Doctrine\DBAL\Types\Types;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version21000Date20201202095923 extends SimpleMigrationStep {
+	/**
+	 * @param IOutput $output
+	 * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+	 * @param array $options
+	 * @return null|ISchemaWrapper
+	 */
+	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+		/** @var ISchemaWrapper $schema */
+		$schema = $schemaClosure();
+
+		if (!$schema->hasTable('accounts_data')) {
+			$table = $schema->createTable('accounts_data');
+			$table->addColumn('id', Types::BIGINT, [
+				'autoincrement' => true,
+				'notnull' => true,
+				'length' => 20,
+			]);
+			$table->addColumn('uid', Types::STRING, [
+				'notnull' => true,
+				'length' => 64,
+			]);
+			$table->addColumn('name', Types::STRING, [
+				'notnull' => true,
+				'length' => 64,
+			]);
+			$table->addColumn('value', Types::STRING, [
+				'notnull' => false,
+				'length' => 255,
+				'default' => '',
+			]);
+			$table->setPrimaryKey(['id']);
+			$table->addIndex(['uid'], 'accounts_data_uid');
+			$table->addIndex(['name'], 'accounts_data_name');
+			$table->addIndex(['value'], 'accounts_data_value');
+
+			return $schema;
+		}
+
+		return null;
+	}
+}
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 214f148fa943c60a0b984680db1e7c56df4a8d94..22c8589f73b7ae377191b66c850e1d41deda94aa 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -216,6 +216,12 @@
 							type: OC.SetupChecks.MESSAGE_TYPE_WARNING
 						});
 					}
+					if (!data.isDefaultPhoneRegionSet) {
+						messages.push({
+							msg: t('core', 'Your installation has no default phone region set. This is required to be able to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add "default_phone_region" with the respective ISO 3166-1 code of the wished region.'),
+							type: OC.SetupChecks.MESSAGE_TYPE_INFO
+						});
+					}
 					if (data.cronErrors.length > 0) {
 						var listOfCronErrors = "";
 						data.cronErrors.forEach(function(element){
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index a0a3c2a4ba9a1080efa24776921b358ee2ff7dd6..c3cddb88a9d7535a2e86b3177a79a5b81f2c4916 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -251,6 +251,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -306,6 +307,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -362,6 +364,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -416,6 +419,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -468,6 +472,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -522,6 +527,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -574,6 +580,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -626,6 +633,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -678,6 +686,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -751,6 +760,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -804,6 +814,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -857,6 +868,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -910,6 +922,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -962,6 +975,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: true,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -1014,6 +1028,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
 					reverseProxyDocs: 'https://docs.nextcloud.com/foo/bar.html',
 					reverseProxyGeneratedURL: 'http://server',
@@ -1067,6 +1082,7 @@ describe('OC.SetupChecks tests', function() {
 					recommendedPHPModules: [],
 					pendingBigIntConversionColumns: [],
 					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: true,
 					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: false,
 					reverseProxyGeneratedURL: 'https://server',
 				})
@@ -1080,6 +1096,59 @@ describe('OC.SetupChecks tests', function() {
 				done();
 			});
 		});
+
+		it('should return an info if there is no default phone region', function(done) {
+			var async = OC.SetupChecks.checkSetup();
+
+			suite.server.requests[0].respond(
+				200,
+				{
+					'Content-Type': 'application/json',
+				},
+				JSON.stringify({
+					hasFileinfoInstalled: true,
+					isGetenvServerWorking: true,
+					isReadOnlyConfig: false,
+					hasWorkingFileLocking: true,
+					hasValidTransactionIsolationLevel: true,
+					suggestedOverwriteCliURL: '',
+					isRandomnessSecure: true,
+					securityDocs: 'https://docs.owncloud.org/myDocs.html',
+					serverHasInternetConnectionProblems: false,
+					isMemcacheConfigured: true,
+					forwardedForHeadersWorking: true,
+					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
+					isOpcacheProperlySetup: true,
+					hasOpcacheLoaded: true,
+					isSettimelimitAvailable: true,
+					hasFreeTypeSupport: true,
+					missingIndexes: [],
+					missingPrimaryKeys: [],
+					missingColumns: [],
+					cronErrors: [],
+					cronInfo: {
+						diffInSeconds: 0
+					},
+					isMemoryLimitSufficient: true,
+					appDirsWithDifferentOwner: [],
+					recommendedPHPModules: [],
+					pendingBigIntConversionColumns: [],
+					isMysqlUsedWithoutUTF8MB4: false,
+					isDefaultPhoneRegionSet: false,
+					isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
+					reverseProxyGeneratedURL: 'https://server',
+				})
+			);
+
+			async.done(function( data, s, x ){
+				expect(data).toEqual([{
+					msg: 'Your installation has no default phone region set. This is required to be able to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add "default_phone_region" with the respective ISO 3166-1 code of the wished region.',
+					type: OC.SetupChecks.MESSAGE_TYPE_INFO
+				}]);
+				done();
+			});
+		});
 	});
 
 	describe('checkGeneric', function() {
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 0400e681090567fcb3b400129d1b4d699261eb13..b7dbc6675d237f81c74fbefc59922ebd591ce1d6 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -930,6 +930,7 @@ return array(
     'OC\\Core\\Migrations\\Version20000Date20201109081918' => $baseDir . '/core/Migrations/Version20000Date20201109081918.php',
     'OC\\Core\\Migrations\\Version20000Date20201109081919' => $baseDir . '/core/Migrations/Version20000Date20201109081919.php',
     'OC\\Core\\Migrations\\Version20000Date20201111081915' => $baseDir . '/core/Migrations/Version20000Date20201111081915.php',
+    'OC\\Core\\Migrations\\Version21000Date20201202095923' => $baseDir . '/core/Migrations/Version21000Date20201202095923.php',
     'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
     'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
     'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
@@ -1266,6 +1267,7 @@ return array(
     'OC\\Repair\\NC20\\EncryptionMigration' => $baseDir . '/lib/private/Repair/NC20/EncryptionMigration.php',
     'OC\\Repair\\NC20\\ShippedDashboardEnable' => $baseDir . '/lib/private/Repair/NC20/ShippedDashboardEnable.php',
     'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => $baseDir . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
+    'OC\\Repair\\NC21\\ValidatePhoneNumber' => $baseDir . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
     'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php',
     'OC\\Repair\\Owncloud\\DropAccountTermsTable' => $baseDir . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
     'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index b9b4f2f307b8c8633c8dcdff02a532188eb7193f..a8984b486f3cb90d098ccb90ccf8d8fd667b7d53 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -959,6 +959,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Core\\Migrations\\Version20000Date20201109081918' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201109081918.php',
         'OC\\Core\\Migrations\\Version20000Date20201109081919' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201109081919.php',
         'OC\\Core\\Migrations\\Version20000Date20201111081915' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201111081915.php',
+        'OC\\Core\\Migrations\\Version21000Date20201202095923' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20201202095923.php',
         'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
         'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
         'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
@@ -1295,6 +1296,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Repair\\NC20\\EncryptionMigration' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/EncryptionMigration.php',
         'OC\\Repair\\NC20\\ShippedDashboardEnable' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/ShippedDashboardEnable.php',
         'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
+        'OC\\Repair\\NC21\\ValidatePhoneNumber' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
         'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php',
         'OC\\Repair\\Owncloud\\DropAccountTermsTable' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
         'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index d18555d296c12cb6f874ffb3bd6f5f1c7656f383..05feaf87b8ff70f64eb18c5167f770eb5f94e7c8 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -30,10 +30,16 @@
 
 namespace OC\Accounts;
 
+use libphonenumber\NumberParseException;
+use libphonenumber\PhoneNumber;
+use libphonenumber\PhoneNumberFormat;
+use libphonenumber\PhoneNumberUtil;
 use OCA\Settings\BackgroundJobs\VerifyUserData;
 use OCP\Accounts\IAccount;
 use OCP\Accounts\IAccountManager;
 use OCP\BackgroundJob\IJobList;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IConfig;
 use OCP\IDBConnection;
 use OCP\IUser;
 use Psr\Log\LoggerInterface;
@@ -55,9 +61,15 @@ class AccountManager implements IAccountManager {
 	/** @var  IDBConnection database connection */
 	private $connection;
 
+	/** @var IConfig */
+	private $config;
+
 	/** @var string table name */
 	private $table = 'accounts';
 
+	/** @var string table name */
+	private $dataTable = 'accounts_data';
+
 	/** @var EventDispatcherInterface */
 	private $eventDispatcher;
 
@@ -68,24 +80,70 @@ class AccountManager implements IAccountManager {
 	private $logger;
 
 	public function __construct(IDBConnection $connection,
+								IConfig $config,
 								EventDispatcherInterface $eventDispatcher,
 								IJobList $jobList,
 								LoggerInterface $logger) {
 		$this->connection = $connection;
+		$this->config = $config;
 		$this->eventDispatcher = $eventDispatcher;
 		$this->jobList = $jobList;
 		$this->logger = $logger;
 	}
 
+	/**
+	 * @param string $input
+	 * @return string Provided phone number in E.164 format when it was a valid number
+	 * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
+	 */
+	protected function parsePhoneNumber(string $input): string {
+		$defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
+
+		if ($defaultRegion === '') {
+			// When no default region is set, only +49… numbers are valid
+			if (strpos($input, '+') !== 0) {
+				throw new \InvalidArgumentException(self::PROPERTY_PHONE);
+			}
+
+			$defaultRegion = 'EN';
+		}
+
+		$phoneUtil = PhoneNumberUtil::getInstance();
+		try {
+			$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
+			if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
+				return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
+			}
+		} catch (NumberParseException $e) {
+		}
+
+		throw new \InvalidArgumentException(self::PROPERTY_PHONE);
+	}
+
 	/**
 	 * update user record
 	 *
 	 * @param IUser $user
-	 * @param $data
+	 * @param array $data
+	 * @param bool $throwOnData Set to true if you can inform the user about invalid data
+	 * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
+	 * @throws \InvalidArgumentException Message is the property that was invalid
 	 */
-	public function updateUser(IUser $user, $data) {
+	public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
 		$userData = $this->getUser($user);
 		$updated = true;
+
+		if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
+			try {
+				$data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
+			} catch (\InvalidArgumentException $e) {
+				if ($throwOnData) {
+					throw $e;
+				}
+				$data[self::PROPERTY_PHONE]['value'] = '';
+			}
+		}
+
 		if (empty($userData)) {
 			$this->insertNewUser($user, $data);
 		} elseif ($userData !== $data) {
@@ -103,6 +161,8 @@ class AccountManager implements IAccountManager {
 				new GenericEvent($user, $data)
 			);
 		}
+
+		return $data;
 	}
 
 	/**
@@ -116,6 +176,21 @@ class AccountManager implements IAccountManager {
 		$query->delete($this->table)
 			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
 			->execute();
+
+		$this->deleteUserData($user);
+	}
+
+	/**
+	 * delete user from accounts table
+	 *
+	 * @param IUser $user
+	 */
+	public function deleteUserData(IUser $user): void {
+		$uid = $user->getUID();
+		$query = $this->connection->getQueryBuilder();
+		$query->delete($this->dataTable)
+			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
+			->execute();
 	}
 
 	/**
@@ -153,6 +228,24 @@ class AccountManager implements IAccountManager {
 		return $userDataArray;
 	}
 
+	public function searchUsers(string $property, array $values): array {
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')
+			->from($this->dataTable)
+			->where($query->expr()->eq('name', $query->createNamedParameter($property)))
+			->andWhere($query->expr()->in('value', $query->createNamedParameter($values, IQueryBuilder::PARAM_STR_ARRAY)));
+
+		$result = $query->execute();
+		$matches = [];
+
+		while ($row = $result->fetch()) {
+			$matches[$row['value']] = $row['uid'];
+		}
+		$result->closeCursor();
+
+		return $matches;
+	}
+
 	/**
 	 * check if we need to ask the server for email verification, if yes we create a cronjob
 	 *
@@ -173,7 +266,7 @@ class AccountManager implements IAccountManager {
 					'lastRun' => time()
 				]
 			);
-			$newData[AccountManager::PROPERTY_EMAIL]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+			$newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
 		}
 
 		return $newData;
@@ -256,7 +349,7 @@ class AccountManager implements IAccountManager {
 	 * @param IUser $user
 	 * @param array $data
 	 */
-	protected function insertNewUser(IUser $user, $data) {
+	protected function insertNewUser(IUser $user, array $data): void {
 		$uid = $user->getUID();
 		$jsonEncodedData = json_encode($data);
 		$query = $this->connection->getQueryBuilder();
@@ -268,6 +361,9 @@ class AccountManager implements IAccountManager {
 				]
 			)
 			->execute();
+
+		$this->deleteUserData($user);
+		$this->writeUserData($user, $data);
 	}
 
 	/**
@@ -276,7 +372,7 @@ class AccountManager implements IAccountManager {
 	 * @param IUser $user
 	 * @param array $data
 	 */
-	protected function updateExistingUser(IUser $user, $data) {
+	protected function updateExistingUser(IUser $user, array $data): void {
 		$uid = $user->getUID();
 		$jsonEncodedData = json_encode($data);
 		$query = $this->connection->getQueryBuilder();
@@ -284,6 +380,30 @@ class AccountManager implements IAccountManager {
 			->set('data', $query->createNamedParameter($jsonEncodedData))
 			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
 			->execute();
+
+		$this->deleteUserData($user);
+		$this->writeUserData($user, $data);
+	}
+
+	protected function writeUserData(IUser $user, array $data): void {
+		$query = $this->connection->getQueryBuilder();
+		$query->insert($this->dataTable)
+			->values(
+				[
+					'uid' => $query->createNamedParameter($user->getUID()),
+					'name' => $query->createParameter('name'),
+					'value' => $query->createParameter('value'),
+				]
+			);
+		foreach ($data as $propertyName => $property) {
+			if ($propertyName === self::PROPERTY_AVATAR) {
+				continue;
+			}
+
+			$query->setParameter('name', $propertyName)
+				->setParameter('value', $property['value']);
+			$query->execute();
+		}
 	}
 
 	/**
diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php
index 82f55f5a9d1a2b310cf3ab6841b14a3b1e7a0b5c..c5e7c34deaf545f5b2746d2c06158645bc830b28 100644
--- a/lib/private/Accounts/Hooks.php
+++ b/lib/private/Accounts/Hooks.php
@@ -24,6 +24,7 @@
 
 namespace OC\Accounts;
 
+use OCP\Accounts\IAccountManager;
 use OCP\IUser;
 use Psr\Log\LoggerInterface;
 
@@ -61,14 +62,14 @@ class Hooks {
 
 		switch ($feature) {
 			case 'eMailAddress':
-				if ($accountData[AccountManager::PROPERTY_EMAIL]['value'] !== $newValue) {
-					$accountData[AccountManager::PROPERTY_EMAIL]['value'] = $newValue;
+				if ($accountData[IAccountManager::PROPERTY_EMAIL]['value'] !== $newValue) {
+					$accountData[IAccountManager::PROPERTY_EMAIL]['value'] = $newValue;
 					$accountManager->updateUser($user, $accountData);
 				}
 				break;
 			case 'displayName':
-				if ($accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) {
-					$accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue;
+				if ($accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) {
+					$accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue;
 					$accountManager->updateUser($user, $accountData);
 				}
 				break;
diff --git a/lib/private/Repair.php b/lib/private/Repair.php
index ec7483555672b7e773b0fd2006f20b9d30556557..847a41aeb254f343ac8d431bd6504a11d89ebc17 100644
--- a/lib/private/Repair.php
+++ b/lib/private/Repair.php
@@ -53,6 +53,7 @@ use OC\Repair\NC20\EncryptionLegacyCipher;
 use OC\Repair\NC20\EncryptionMigration;
 use OC\Repair\NC20\ShippedDashboardEnable;
 use OC\Repair\NC21\AddCheckForUserCertificatesJob;
+use OC\Repair\NC21\ValidatePhoneNumber;
 use OC\Repair\OldGroupMembershipShares;
 use OC\Repair\Owncloud\DropAccountTermsTable;
 use OC\Repair\Owncloud\SaveAccountsTableData;
@@ -177,7 +178,8 @@ class Repair implements IOutput {
 	 */
 	public static function getExpensiveRepairSteps() {
 		return [
-			new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager())
+			new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()),
+			\OC::$server->get(ValidatePhoneNumber::class),
 		];
 	}
 
diff --git a/lib/private/Repair/NC21/ValidatePhoneNumber.php b/lib/private/Repair/NC21/ValidatePhoneNumber.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e25ff26b7efae99cd7a2c064495bc09e68f9529
--- /dev/null
+++ b/lib/private/Repair/NC21/ValidatePhoneNumber.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Repair\NC21;
+
+use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ValidatePhoneNumber implements IRepairStep {
+
+	/** @var IConfig */
+	protected $config;
+	/** @var IUserManager */
+	protected $userManager;
+	/** @var AccountManager */
+	private $accountManager;
+
+	public function __construct(IUserManager $userManager,
+								AccountManager $accountManager,
+								IConfig $config) {
+		$this->config = $config;
+		$this->userManager = $userManager;
+		$this->accountManager = $accountManager;
+	}
+
+	public function getName(): string {
+		return 'Validate the phone number and store it in a known format for search';
+	}
+
+	private function shouldRun(): bool {
+		return true;
+	}
+
+	public function run(IOutput $output): void {
+		if ($this->config->getSystemValueString('default_phone_region', '') === '') {
+			throw new \Exception('Can not validate phone numbers without `default_phone_region` being set in the config file');
+		}
+
+		$numUpdated = 0;
+		$numRemoved = 0;
+
+		$this->userManager->callForSeenUsers(function (IUser $user) use (&$numUpdated, &$numRemoved) {
+			$account = $this->accountManager->getUser($user);
+
+			if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== '') {
+				$updated = $this->accountManager->updateUser($user, $account);
+
+				if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== $updated[IAccountManager::PROPERTY_PHONE]['value']) {
+					if ($updated[IAccountManager::PROPERTY_PHONE]['value'] === '') {
+						$numRemoved++;
+					} else {
+						$numUpdated++;
+					}
+				}
+			}
+		});
+
+		if ($numRemoved > 0 || $numUpdated > 0) {
+			$output->info('Updated ' . $numUpdated . ' entries and cleaned ' . $numRemoved . ' invalid phone numbers');
+		}
+	}
+}
diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php
index 3306abc55f174d0c80c745c7445cf97f3100413a..63824ed94b93ef3c3c1c362229b7c3553257c14b 100644
--- a/lib/public/Accounts/IAccountManager.php
+++ b/lib/public/Accounts/IAccountManager.php
@@ -65,4 +65,15 @@ interface IAccountManager {
 	 * @return IAccount
 	 */
 	public function getAccount(IUser $user): IAccount;
+
+	/**
+	 * Search for users based on account data
+	 *
+	 * @param string $property
+	 * @param string[] $values
+	 * @return array
+	 *
+	 * @since 21.0.0
+	 */
+	public function searchUsers(string $property, array $values): array;
 }
diff --git a/tests/lib/Accounts/AccountsManagerTest.php b/tests/lib/Accounts/AccountsManagerTest.php
index ff75b51d008eb67aba1e8249d20dd6d37913de15..d13d5f4186a5a4de83509724c84e746e40fafb18 100644
--- a/tests/lib/Accounts/AccountsManagerTest.php
+++ b/tests/lib/Accounts/AccountsManagerTest.php
@@ -25,6 +25,7 @@ use OC\Accounts\Account;
 use OC\Accounts\AccountManager;
 use OCP\Accounts\IAccountManager;
 use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
 use OCP\IUser;
 use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Log\LoggerInterface;
@@ -43,6 +44,9 @@ class AccountsManagerTest extends TestCase {
 	/** @var  \OCP\IDBConnection */
 	private $connection;
 
+	/** @var  IConfig|MockObject */
+	private $config;
+
 	/** @var  EventDispatcherInterface|MockObject */
 	private $eventDispatcher;
 
@@ -59,6 +63,7 @@ class AccountsManagerTest extends TestCase {
 		parent::setUp();
 		$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
 		$this->connection = \OC::$server->getDatabaseConnection();
+		$this->config = $this->createMock(IConfig::class);
 		$this->jobList = $this->createMock(IJobList::class);
 		$this->logger = $this->createMock(LoggerInterface::class);
 	}
@@ -77,7 +82,13 @@ class AccountsManagerTest extends TestCase {
 	 */
 	public function getInstance($mockedMethods = null) {
 		return $this->getMockBuilder(AccountManager::class)
-			->setConstructorArgs([$this->connection, $this->eventDispatcher, $this->jobList, $this->logger])
+			->setConstructorArgs([
+				$this->connection,
+				$this->config,
+				$this->eventDispatcher,
+				$this->jobList,
+				$this->logger,
+			])
 			->setMethods($mockedMethods)
 			->getMock();
 	}
@@ -187,9 +198,9 @@ class AccountsManagerTest extends TestCase {
 
 	public function testUpdateExistingUser() {
 		$user = $this->getMockBuilder(IUser::class)->getMock();
-		$user->expects($this->once())->method('getUID')->willReturn('uid');
-		$oldData = ['key' => 'value'];
-		$newData = ['newKey' => 'newValue'];
+		$user->expects($this->atLeastOnce())->method('getUID')->willReturn('uid');
+		$oldData = ['key' => ['value' => 'value']];
+		$newData = ['newKey' => ['value' => 'newValue']];
 
 		$accountManager = $this->getInstance();
 		$this->addDummyValuesToTable('uid', $oldData);
@@ -201,10 +212,10 @@ class AccountsManagerTest extends TestCase {
 	public function testInsertNewUser() {
 		$user = $this->getMockBuilder(IUser::class)->getMock();
 		$uid = 'uid';
-		$data = ['key' => 'value'];
+		$data = ['key' => ['value' => 'value']];
 
 		$accountManager = $this->getInstance();
-		$user->expects($this->once())->method('getUID')->willReturn($uid);
+		$user->expects($this->atLeastOnce())->method('getUID')->willReturn($uid);
 		$this->assertNull($this->getDataFromTable($uid));
 		$this->invokePrivate($accountManager, 'insertNewUser', [$user, $data]);
 
@@ -293,4 +304,32 @@ class AccountsManagerTest extends TestCase {
 			->willReturn($data);
 		$this->assertEquals($expected, $accountManager->getAccount($user));
 	}
+
+	public function dataParsePhoneNumber(): array {
+		return [
+			['0711 / 25 24 28-90', 'DE', '+4971125242890'],
+			['0711 / 25 24 28-90', '', null],
+			['+49 711 / 25 24 28-90', '', '+4971125242890'],
+		];
+	}
+
+	/**
+	 * @dataProvider dataParsePhoneNumber
+	 * @param string $phoneInput
+	 * @param string $defaultRegion
+	 * @param string|null $phoneNumber
+	 */
+	public function testParsePhoneNumber(string $phoneInput, string $defaultRegion, ?string $phoneNumber): void {
+		$this->config->method('getSystemValueString')
+			->willReturn($defaultRegion);
+
+		$instance = $this->getInstance();
+
+		if ($phoneNumber === null) {
+			$this->expectException(\InvalidArgumentException::class);
+			self::invokePrivate($instance, 'parsePhoneNumber', [$phoneInput]);
+		} else {
+			self::assertEquals($phoneNumber, self::invokePrivate($instance, 'parsePhoneNumber', [$phoneInput]));
+		}
+	}
 }
diff --git a/tests/lib/Accounts/HooksTest.php b/tests/lib/Accounts/HooksTest.php
index 39003b2ef235039b3c9cce9d22718d52b5c3a01f..8af9e209034dfc2b6e9ca96a35ff6808457deb80 100644
--- a/tests/lib/Accounts/HooksTest.php
+++ b/tests/lib/Accounts/HooksTest.php
@@ -23,6 +23,7 @@ namespace Test\Accounts;
 
 use OC\Accounts\AccountManager;
 use OC\Accounts\Hooks;
+use OCP\Accounts\IAccountManager;
 use OCP\IUser;
 use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Log\LoggerInterface;
@@ -77,11 +78,11 @@ class HooksTest extends TestCase {
 			$this->accountManager->expects($this->once())->method('getUser')->willReturn($data);
 			$newData = $data;
 			if ($setEmail) {
-				$newData[AccountManager::PROPERTY_EMAIL]['value'] = $params['value'];
+				$newData[IAccountManager::PROPERTY_EMAIL]['value'] = $params['value'];
 				$this->accountManager->expects($this->once())->method('updateUser')
 					->with($params['user'], $newData);
 			} elseif ($setDisplayName) {
-				$newData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $params['value'];
+				$newData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $params['value'];
 				$this->accountManager->expects($this->once())->method('updateUser')
 					->with($params['user'], $newData);
 			} else {
@@ -98,48 +99,48 @@ class HooksTest extends TestCase {
 			[
 				['feature' => '', 'value' => ''],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => ''],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+					IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
 				],
 				false, false, true
 			],
 			[
 				['user' => $user, 'value' => ''],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => ''],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+					IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
 				],
 				false, false, true
 			],
 			[
 				['user' => $user, 'feature' => ''],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => ''],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+					IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
 				],
 				false, false, true
 			],
 			[
 				['user' => $user, 'feature' => 'foo', 'value' => 'bar'],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
 				],
 				false, false, false
 			],
 			[
 				['user' => $user, 'feature' => 'eMailAddress', 'value' => 'newMail@example.com'],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
 				],
 				true, false, false
 			],
 			[
 				['user' => $user, 'feature' => 'displayName', 'value' => 'newDisplayName'],
 				[
-					AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
-					AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+					IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+					IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
 				],
 				false, true, false
 			],
diff --git a/version.php b/version.php
index 24e3e8db6aeba242f423ac9702d9a521ec012bfa..299855816a9fd772158e86c49f26b9673d676115 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
 // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
 // when updating major/minor version number.
 
-$OC_Version = [21, 0, 0, 7];
+$OC_Version = [21, 0, 0, 8];
 
 // The human readable string
 $OC_VersionString = '21.0.0 alpha';