Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions docs/user-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
************************************
Expand Down
19 changes: 18 additions & 1 deletion src/Firebase/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,7 +60,7 @@
/**
* @internal
*/
final class Auth implements Contract\Auth
final class Auth implements Contract\Auth, FederatedUserFetcher
{
private readonly Parser $jwtParser;

Expand Down Expand Up @@ -210,6 +211,22 @@
return UserRecord::fromResponseData($data['users'][0]);
}

public function getUserByProviderUid(Stringable|string $providerId, Stringable|string $providerUid): UserRecord

Check warning on line 214 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L214

Added line #L214 was not covered by tests
{
$providerId = (string) $providerId;
$providerUid = (string) $providerUid;

Check warning on line 217 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L216-L217

Added lines #L216 - L217 were not covered by tests

$response = $this->client->getUserByProviderUid($providerId, $providerUid);

Check warning on line 219 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L219

Added line #L219 was not covered by tests

$data = Json::decode((string) $response->getBody(), true);

Check warning on line 221 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L221

Added line #L221 was not covered by tests

if (empty($data['users'][0])) {
throw new UserNotFound("No user with federated account ID '{$providerId}:{$providerUid}' found.");

Check warning on line 224 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L223-L224

Added lines #L223 - L224 were not covered by tests
}

return UserRecord::fromResponseData($data['users'][0]);

Check warning on line 227 in src/Firebase/Auth.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth.php#L227

Added line #L227 was not covered by tests
}

public function createAnonymousUser(): UserRecord
{
return $this->createUser(CreateUser::new());
Expand Down
10 changes: 10 additions & 0 deletions src/Firebase/Auth/ApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@
return $this->requestApi($url, ['phoneNumber' => [$phoneNumber]]);
}

/**
* @throws AuthException
*/
public function getUserByProviderUid(string $providerId, string $uid): ResponseInterface

Check warning on line 124 in src/Firebase/Auth/ApiClient.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth/ApiClient.php#L124

Added line #L124 was not covered by tests
{
$url = $this->awareAuthResourceUrlBuilder->getUrl('/accounts:lookup');

Check warning on line 126 in src/Firebase/Auth/ApiClient.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth/ApiClient.php#L126

Added line #L126 was not covered by tests

return $this->requestApi($url, ['federatedUserId' => [['providerId' => $providerId, 'rawId' => $uid]]]);

Check warning on line 128 in src/Firebase/Auth/ApiClient.php

View check run for this annotation

Codecov / codecov/patch

src/Firebase/Auth/ApiClient.php#L128

Added line #L128 was not covered by tests
}

/**
* @throws AuthException
*/
Expand Down
26 changes: 26 additions & 0 deletions src/Firebase/Contract/Transitional/FederatedUserFetcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Kreait\Firebase\Contract\Transitional;

use Kreait\Firebase\Auth\UserRecord;
use Kreait\Firebase\Exception;
use Kreait\Firebase\Exception\Auth\UserNotFound;
use Stringable;

/**
* @TODO: This interface is intended to be integrated into the Auth interface on the next major release.
*/
interface FederatedUserFetcher
{
/**
* @param Stringable|non-empty-string $providerId
* @param Stringable|non-empty-string $providerUid
*
* @throws UserNotFound
* @throws Exception\AuthException
* @throws Exception\FirebaseException
*/
public function getUserByProviderUid(Stringable|string $providerId, Stringable|string $providerUid): UserRecord;
}
45 changes: 45 additions & 0 deletions tests/Integration/AuthTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Kreait\Firebase\Auth\SignIn\FailedToSignIn;
use Kreait\Firebase\Auth\UserRecord;
use Kreait\Firebase\Contract\Auth;
use Kreait\Firebase\Contract\Transitional\FederatedUserFetcher;
use Kreait\Firebase\Exception\Auth\InvalidOobCode;
use Kreait\Firebase\Exception\Auth\RevokedIdToken;
use Kreait\Firebase\Exception\Auth\RevokedSessionCookie;
Expand Down Expand Up @@ -520,6 +521,50 @@ public function getUserByNonExistingPhoneNumber(): void
$this->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
{
Expand Down
Loading