diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da5d6de7..bc7fe1fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,34 @@ Please read about the future of the Firebase Admin PHP SDK on the ## [Unreleased] +### Added + +* You can now get a user by their federated identity provider (e.g. Google, Facebook, etc.) UID with + `Kreait\Firebase\Auth::getUserByProviderUid()`. ([#1000](https://github.com/kreait/firebase-php/pull/1000)) + Since this method couldn't be added to the `Kreait\Firebase\Contract\Auth` interface without causing a breaking + change, a new transitional interface/contract named `Kreait\Firebase\Contract\Transitional\FederatedUserFetcher` + was added. This interface will be removed in the next major version of the SDK. + There are several ways to check if you can use the `getUserByProviderUid()` method: + ```php + use Kreait\Firebase\Contract\Transitional\FederatedUserFetcher; + use Kreait\Firebase\Factory; + + $auth = (new Factory())->createAuth(); + // The return type is Kreait\Firebase\Contract\Auth, which doesn't have the method + + if (method_exists($auth, 'getUserByProviderUid')) { + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + + if ($auth instanceof \Kreait\Firebase\Auth) { // This is the implementation, not the interface + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + + if ($auth instanceof FederatedUserFetcher) { + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + ``` + ## [7.19.0] - 2025-06-14 ### Added diff --git a/docs/user-management.rst b/docs/user-management.rst index a016a8fc0..cd52755c2 100644 --- a/docs/user-management.rst +++ b/docs/user-management.rst @@ -144,10 +144,53 @@ Get information about a specific user $user = $auth->getUser('some-uid'); $user = $auth->getUserByEmail('user@example.com'); $user = $auth->getUserByPhoneNumber('+49-123-456789'); + // For `getUserByProviderUid()` please see the section below. + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); } catch (\Kreait\Firebase\Exception\Auth\UserNotFound $e) { echo $e->getMessage(); } +*************************************************** +Get information about a user by federated provider +*************************************************** + +You can retrieve a user by their federated identity provider UID (e.g. Google, Facebook, etc.): + +.. code-block:: php + + try { + $googleUser = $auth->getUserByProviderUid('google.com', 'google-uid'); + $facebookUser = $auth->getUserByProviderUid('facebook.com', 'facebook-uid'); + } catch (\Kreait\Firebase\Exception\Auth\UserNotFound $e) { + echo $e->getMessage(); + } + +.. note:: + Since this method couldn't be added to the ``Kreait\Firebase\Contract\Auth`` interface without causing a breaking + change, a new transitional interface/contract named ``Kreait\Firebase\Contract\Transitional\FederatedUserFetcher`` + was added. This interface will be removed in the next major version of the SDK. + +There are several ways to check if you can use the ``getUserByProviderUid()`` method: + +.. code-block:: php + + use Kreait\Firebase\Contract\Transitional\FederatedUserFetcher; + use Kreait\Firebase\Factory; + + $auth = (new Factory())->createAuth(); + + if (method_exists($auth, 'getUserByProviderUid')) { + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + + if ($auth instanceof \Kreait\Firebase\Auth) { // This is the implementation, not the interface + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + + if ($auth instanceof FederatedUserFetcher) { + $user = $auth->getUserByProviderUid('google.com', 'google-uid'); + } + ************************************ Get information about multiple users ************************************ diff --git a/src/Firebase/Auth.php b/src/Firebase/Auth.php index e637f93f0..cb6409bee 100644 --- a/src/Firebase/Auth.php +++ b/src/Firebase/Auth.php @@ -24,6 +24,7 @@ use Kreait\Firebase\Auth\SignInWithRefreshToken; use Kreait\Firebase\Auth\UserQuery; use Kreait\Firebase\Auth\UserRecord; +use Kreait\Firebase\Contract\Transitional\FederatedUserFetcher; use Kreait\Firebase\Exception\Auth\AuthError; use Kreait\Firebase\Exception\Auth\FailedToVerifySessionCookie; use Kreait\Firebase\Exception\Auth\FailedToVerifyToken; @@ -59,7 +60,7 @@ /** * @internal */ -final class Auth implements Contract\Auth +final class Auth implements Contract\Auth, FederatedUserFetcher { private readonly Parser $jwtParser; @@ -210,6 +211,22 @@ public function getUserByPhoneNumber(Stringable|string $phoneNumber): UserRecord return UserRecord::fromResponseData($data['users'][0]); } + public function getUserByProviderUid(Stringable|string $providerId, Stringable|string $providerUid): UserRecord + { + $providerId = (string) $providerId; + $providerUid = (string) $providerUid; + + $response = $this->client->getUserByProviderUid($providerId, $providerUid); + + $data = Json::decode((string) $response->getBody(), true); + + if (empty($data['users'][0])) { + throw new UserNotFound("No user with federated account ID '{$providerId}:{$providerUid}' found."); + } + + return UserRecord::fromResponseData($data['users'][0]); + } + public function createAnonymousUser(): UserRecord { return $this->createUser(CreateUser::new()); diff --git a/src/Firebase/Auth/ApiClient.php b/src/Firebase/Auth/ApiClient.php index 7775c6153..749a53bd9 100644 --- a/src/Firebase/Auth/ApiClient.php +++ b/src/Firebase/Auth/ApiClient.php @@ -118,6 +118,16 @@ public function getUserByPhoneNumber(string $phoneNumber): ResponseInterface return $this->requestApi($url, ['phoneNumber' => [$phoneNumber]]); } + /** + * @throws AuthException + */ + public function getUserByProviderUid(string $providerId, string $uid): ResponseInterface + { + $url = $this->awareAuthResourceUrlBuilder->getUrl('/accounts:lookup'); + + return $this->requestApi($url, ['federatedUserId' => [['providerId' => $providerId, 'rawId' => $uid]]]); + } + /** * @throws AuthException */ diff --git a/src/Firebase/Contract/Transitional/FederatedUserFetcher.php b/src/Firebase/Contract/Transitional/FederatedUserFetcher.php new file mode 100644 index 000000000..505802ae0 --- /dev/null +++ b/src/Firebase/Contract/Transitional/FederatedUserFetcher.php @@ -0,0 +1,26 @@ +auth->getUserByPhoneNumber($phoneNumber); } + #[Test] + public function getUserByProviderUid(): void + { + if (Util::authEmulatorHost() === null) { + $this->markTestSkipped('Getting user by provider UID can only be tested with the Firebase emulator.'); + } + + $auth = $this->auth; + if (!($auth instanceof FederatedUserFetcher)) { + $this->markTestSkipped('This test requires a FederatedUserFetcher implementation.'); + } + + $phoneNumber = '+1234567'.random_int(1000, 9999); + + $user = $this->auth->createUser([ + 'phoneNumber' => $phoneNumber, + ]); + + $check = $auth->getUserByProviderUid('phone', "$phoneNumber"); + + try { + $this->assertSame($user->uid, $check->uid); + } finally { + $auth->deleteUser($user->uid); + } + + } + + #[Test] + public function getUserByNonExistingProviderUid(): void + { + if (Util::authEmulatorHost() === null) { + $this->markTestSkipped('Getting user by provider UID can only be tested with the Firebase emulator.'); + } + + $auth = $this->auth; + if (!($auth instanceof FederatedUserFetcher)) { + $this->markTestSkipped('This test requires a FederatedUserFetcher implementation.'); + } + + $this->expectException(UserNotFound::class); + $auth->getUserByProviderUid('phone', '+192837465'); + } + #[Test] public function createUser(): void {