Skip to content

Commit

Permalink
feat: support configuration of credentials via config array
Browse files Browse the repository at this point in the history
  • Loading branch information
clemblanco committed Sep 26, 2023
1 parent 1ddab63 commit 954fc85
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 26 deletions.
57 changes: 43 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ Please read about the future of the Firebase Admin PHP SDK on the

---

- [Installation](#installation)
- [Laravel](#laravel)
- [Configuration](#configuration)
- [Usage](#usage)
- [Multiple projects](#multiple-projects)
- [Supported Versions](#supported-versions)
- [License](#license)
- [Installation](#installation)
- [Laravel](#laravel)
- [Configuration](#configuration)
- [Credentials with JSON files](#credentials-with-json-files-auto-discovered)
- [Credentials with arrays](#credentials-with-arrays)
- [Usage](#usage)
- [Multiple projects](#multiple-projects)
- [Supported Versions](#supported-versions)
- [License](#license)

## Installation

Expand All @@ -37,12 +39,6 @@ composer require kreait/laravel-firebase
In order to access a Firebase project and its related services using a server SDK, requests must be authenticated.
For server-to-server communication this is done with a Service Account.

The package uses auto discovery for the default project to find the credentials needed for authenticating requests to
the Firebase APIs by inspecting certain environment variables and looking into Google's well known path(s).

If you don't want a service account to be auto-discovered, provide it by setting the `GOOGLE_APPLICATION_CREDENTIALS`
environment variable or by adapting the package configuration.

If you don't already have generated a Service Account, you can do so by following the instructions from the
official documentation pages at https://firebase.google.com/docs/admin/setup#initialize_the_sdk.

Expand All @@ -64,6 +60,39 @@ by copying it to your local `config` directory or by defining the environment va
php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" --tag=config
```

### Credentials with JSON files auto-discovered

The package uses auto discovery for the default project to find the credentials needed for authenticating requests to
the Firebase APIs by inspecting certain environment variables and looking into Google's well known path(s).

If you don't want a service account to be auto-discovered, provide it by setting the `FIREBASE_CREDENTIALS` or `GOOGLE_APPLICATION_CREDENTIALS` environment variable or by adapting the package configuration, like so for example:

```.env
FIREBASE_CREDENTIALS=storage/app/firebase-auth.json
```

### Credentials with JSON arrays

If you prefer to have more control over the configuration items required to configure the credentials, you can also transpose the Service Account JSON file as an array within your `config/firebase.php` file.

```php
'credentials' => [
'type' => 'service_account',
'project_id' => 'some-project-123',
'private_key_id' => '123456789',
'private_key' => '-----BEGIN PRIVATE KEY-----\nFOO_BAR_123456789\n-----END PRIVATE KEY-----\n',
'client_email' => '[email protected]',
'client_id' => '123456789',
'auth_uri' => 'https://accounts.google.com/o/oauth2/auth',
'token_uri' => 'https://oauth2.googleapis.com/token',
'auth_provider_x509_cert_url' => 'https://www.googleapis.com/oauth2/v1/certs',
'client_x509_cert_url' => 'https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-cwiuo%40some-project-123.iam.gserviceaccount.com',
'universe_domain' => 'googleapis.com',
],
```

Feel free to introduce more environment variables as you see fit, for example `env('FIREBASE_CREDENTIALS_APP_PROJECT_ID')`.

## Usage

Once you have retrieved a component, please refer to the [documentation of the Firebase PHP Admin SDK](https://firebase-php.readthedocs.io)
Expand Down Expand Up @@ -96,7 +125,7 @@ Earlier versions will receive security fixes as long as their **lowest** SDK req
can find the currently supported versions and support options in the [SDK's README](https://github.com/kreait/firebase-php).

| Version | Initial Release | Supported SDK Versions | Supported Laravel Versions | Status |
|---------|-----------------|------------------------|----------------------------|-------------|
| ------- | --------------- | ---------------------- | -------------------------- | ----------- |
| `5.x` | 13 Jan 2023 | `^7.0` | `^9.0` | Active |
| `4.x` | 09 Jan 2022 | `^6.0` | `^8.0` | End of life |
| `3.x` | 01 Nov 2020 | `^5.24` | `^6.0, ^7.0, ^8.0` | End of life |
Expand Down
4 changes: 1 addition & 3 deletions config/firebase.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@
* first time you try to access a component of the Firebase Admin SDK.
*
*/
'credentials' => [
'file' => env('FIREBASE_CREDENTIALS', env('GOOGLE_APPLICATION_CREDENTIALS')),
],
'credentials' => env('FIREBASE_CREDENTIALS', env('GOOGLE_APPLICATION_CREDENTIALS')),

/*
* ------------------------------------------------------------------------
Expand Down
10 changes: 7 additions & 3 deletions src/FirebaseProjectManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ protected function configure(string $name): FirebaseProject
$factory = $factory->withTenantId($tenantId);
}

if ($credentials = $config['credentials']['file'] ?? null) {
$resolvedCredentials = $this->resolveCredentials((string) $credentials);
if ($credentials = $config['credentials'] ?? null) {
if (is_string($credentials)) {
$factory = $factory->withServiceAccount($this->resolveCredentials($credentials));
}

$factory = $factory->withServiceAccount($resolvedCredentials);
if (is_array($credentials)) {
$factory = $factory->withServiceAccount($credentials);
}
}

if ($databaseUrl = $config['database']['url'] ?? null) {
Expand Down
38 changes: 33 additions & 5 deletions tests/FirebaseProjectManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class FirebaseProjectManagerTest extends TestCase
{
protected function defineEnvironment($app): void
{
$app['config']->set('firebase.projects.app.credentials.file', __DIR__ . '/_fixtures/service_account.json');
$app['config']->set('firebase.projects.app.credentials', __DIR__ . '/_fixtures/service_account.json');
}

/**
Expand Down Expand Up @@ -66,15 +66,43 @@ public function calls_are_passed_to_default_project(): void
/**
* @test
*/
public function credentials_can_be_configured(): void
public function credentials_can_be_configured_using_a_json_file(): void
{
// Reference credentials
$credentialsPath = \realpath(__DIR__ . '/_fixtures/service_account.json');
$credentials = \json_decode(\file_get_contents($credentialsPath), true);

// Set configuration and retrieve project
$projectName = 'app';
$this->app->config->set('firebase.projects.' . $projectName . '.credentials.file', \realpath(__DIR__ . '/_fixtures/service_account.json'));
$this->app->config->set('firebase.projects.' . $projectName . '.credentials', \realpath(__DIR__ . '/_fixtures/service_account.json'));
$factory = $this->factoryForProject($projectName);

// Retrieve service account
$serviceAccount = $this->getAccessibleProperty($factory, 'serviceAccount')->getValue($factory);

// Validate value
$this->assertSame($credentials, $serviceAccount);
}

/**
* @test
*/
public function credentials_can_be_configured_using_an_array(): void
{
// Set configuration and retrieve project
$projectName = 'app';
$this->app->config->set('firebase.projects.' . $projectName . '.credentials', $credentials = [
'type' => 'service_account',
'project_id' => 'project',
'private_key_id' => 'private_key_id',
'private_key' => '-----BEGIN PRIVATE KEY-----\nsome gibberish\n-----END PRIVATE KEY-----\n',
'client_email' => '[email protected]',
'client_id' => '1234567890',
'auth_uri' => 'https://some.google.tld/o/oauth2/auth',
'token_uri' => 'https://some.google.tld/o/oauth2/token',
'auth_provider_x509_cert_url' => 'https://some.google.tld/oauth2/v1/certs',
'client_x509_cert_url' => 'https://some.google.tld/robot/v1/metadata/x509/user%40project.iam.gserviceaccount.com',
]);
$factory = $this->factoryForProject($projectName);

// Retrieve service account
Expand All @@ -101,8 +129,8 @@ public function projects_can_have_different_credentials(): void
$secondProjectName = 'another-app';

// Set service accounts explicitly
$this->app->config->set('firebase.projects.' . $projectName . '.credentials.file', \realpath(__DIR__ . '/_fixtures/service_account.json'));
$this->app->config->set('firebase.projects.' . $secondProjectName . '.credentials.file', \realpath(__DIR__ . '/_fixtures/another_service_account.json'));
$this->app->config->set('firebase.projects.' . $projectName . '.credentials', \realpath(__DIR__ . '/_fixtures/service_account.json'));
$this->app->config->set('firebase.projects.' . $secondProjectName . '.credentials', \realpath(__DIR__ . '/_fixtures/another_service_account.json'));

// Retrieve factories and service accounts
$factory = $this->factoryForProject($projectName);
Expand Down
2 changes: 1 addition & 1 deletion tests/ServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class ServiceProviderTest extends TestCase
*/
public function it_provides_components(): void
{
$this->app->config->set('firebase.projects.app.credentials.file', \realpath(__DIR__ . '/_fixtures/service_account.json'));
$this->app->config->set('firebase.projects.app.credentials', \realpath(__DIR__ . '/_fixtures/service_account.json'));

$this->assertInstanceOf(Firebase\Contract\AppCheck::class, $this->app->make(Firebase\Contract\AppCheck::class));
$this->assertInstanceOf(Firebase\Contract\Auth::class, $this->app->make(Firebase\Contract\Auth::class));
Expand Down

0 comments on commit 954fc85

Please sign in to comment.