kreait / laravel-firebase

A Laravel package for the Firebase PHP Admin SDK

Home Page:https://github.com/kreait/firebase-php

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SenderId mismatch

JamesPJ opened this issue · comments

Describe the bug

I've 3 projects configured in firebase.php config

But, if I send notification for any project apart from the first one, I'm getting SenderId mismatch error. I've cross checked the issue by moving up and down the projects in the array.
I've no idea why it is only taking the first project.

Installed packages

{
        "php": "^8.0",
        "abrigham/laravel-email-exceptions": "^4.0",
        "barryvdh/laravel-dompdf": "^2.0.1",
        "bonecms/laravel-captcha": "^2.0.1",
        "danhunsaker/laravel-flysystem-others": "*",
        "doctrine/dbal": "^2.10",
        "gecche/laravel-multidomain": "4.2.1",
        "genealabs/laravel-model-caching": "~0.11.7",
        "guzzlehttp/guzzle": "^7.0",
        "intervention/image": "^2.7",
        "kreait/laravel-firebase": "^4.2",
        "laravel-notification-channels/fcm": "^2.7.0",
        "laravel/framework": "8.*",
        "laravel/helpers": "*",
        "laravel/socialite": "^5.8",
        "laravel/tinker": "^2.8",
        "laravelcollective/html": "^6.4.1",
        "league/flysystem": "^1.1",
        "league/flysystem-aws-s3-v3": "^1.0.30",
        "maatwebsite/excel": "^3.1.48",
        "picqer/php-barcode-generator": "^2.2.4",
        "predis/predis": "^2.2",
        "rap2hpoutre/laravel-log-viewer": "^2.3",
        "romanzipp/laravel-turnstile": "^1.1",
        "spatie/laravel-activitylog": "^4.7",
        "spatie/laravel-backup": "7.8.0",
        "spatie/laravel-medialibrary": "9.12.*",
        "sqits/laravel-userstamps": "^0.0.10",
        "tymon/jwt-auth": "^1.0.2"
    }

Steps to reproduce the issue.

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Kreait\Firebase\Exception\Messaging\NotFound;
use Log;
use NotificationChannels\Fcm\FcmChannel;
use NotificationChannels\Fcm\FcmMessage;
use NotificationChannels\Fcm\Resources\AndroidConfig;
use NotificationChannels\Fcm\Resources\AndroidFcmOptions;
use NotificationChannels\Fcm\Resources\AndroidNotification;
use NotificationChannels\Fcm\Resources\ApnsConfig;
use NotificationChannels\Fcm\Resources\ApnsFcmOptions;

class FirebasePush extends Notification implements ShouldQueue
{ 
    use Queueable;

    public $tries = 3;

    protected $msg_title = '';
    protected $msg_text = '';
    protected $data = [];
    protected $image = NULL;
    protected $project = 'myproject';

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($msg_title, $msg_text, $data=[], $image=NULL)
    {
        $this->msg_title = $msg_title;
        $this->msg_text = $msg_text;
        $this->data = $data;
        $this->image = $image;
        $this->project = config('firebase.default');
    }
    public function via($notifiable)
    {
        return [FcmChannel::class];
    }

    public function toFcm($notifiable)
    {
        try {
            $notification = \NotificationChannels\Fcm\Resources\Notification::create()
            ->setTitle($this->msg_title)
            ->setBody($this->msg_text);
            if($this->image) {
                $notification->setImage($this->image);
            }
            if(empty($this->data)) {
                $this->data = ['route' => 'notification'];
            }
            return FcmMessage::create()
                ->setData($this->data)
                ->setNotification($notification)
                ->setAndroid(
                    AndroidConfig::create()
                        ->setFcmOptions(AndroidFcmOptions::create()->setAnalyticsLabel('analytics'))
                        ->setNotification(AndroidNotification::create()->setColor('#324398'))
                )->setApns(
                    ApnsConfig::create()
                        ->setFcmOptions(ApnsFcmOptions::create()->setAnalyticsLabel('analytics_ios')));
        } catch (NotFound $th) {
            $t = $th->errors();
            Log::error($t);
        }
    }

    // optional method when using kreait/laravel-firebase:^3.0, this method can be omitted, defaults to the default project
    public function fcmProject($notifiable, $message)
    {
        return $this->project; // name of the firebase project to use
    }
}

firebase.php

<?php

declare(strict_types=1);

return [
    /*
     * ------------------------------------------------------------------------
     * Default Firebase project
     * ------------------------------------------------------------------------
     */
    'default' => env('FIREBASE_PROJECT', 'myproject'),

    /*
     * ------------------------------------------------------------------------
     * Firebase project configurations
     * ------------------------------------------------------------------------
     */
    'projects' => [
        'myproject' => [
                    'credentials' => [
                        'file' => env('FIREBASE_CREDENTIALS', 'config/myproject.json'),
                        'auto_discovery' => true,
                    ],
                    'auth' => [
                        'tenant_id' => env('FIREBASE_AUTH_TENANT_ID'),
                    ],
                    'database' => [
                        'url' => env('FIREBASE_DATABASE_URL', 'https://myproject.firebaseio.com'),
                    ],
        
                    'dynamic_links' => [
                        'default_domain' => env('FIREBASE_DYNAMIC_LINKS_DEFAULT_DOMAIN'),
                    ],
        
                    'storage' => [
                        'default_bucket' => env('FIREBASE_STORAGE_DEFAULT_BUCKET'),
                    ],
                    'cache_store' => env('FIREBASE_CACHE_STORE', 'file'),
                    'logging' => [
                        'http_log_channel' => env('FIREBASE_HTTP_LOG_CHANNEL'),
                        'http_debug_log_channel' => env('FIREBASE_HTTP_DEBUG_LOG_CHANNEL'),
                    ],
                    'http_client_options' => [
                        'timeout' => env('FIREBASE_HTTP_CLIENT_TIMEOUT'),
                    ],
                ],
        'myprojectOne' => [
                    'credentials' => [
                        'file' => env('FIREBASE_CREDENTIALS', 'config/myprojectOne.json'),
                        'auto_discovery' => true,
                    ],
                    'auth' => [
                        'tenant_id' => env('FIREBASE_AUTH_TENANT_ID'),
                    ],
                    'database' => [
                        'url' => env('FIREBASE_DATABASE_URL', 'https://myproject.firebaseio.com'),
                    ],
        
                    'dynamic_links' => [
                        'default_domain' => env('FIREBASE_DYNAMIC_LINKS_DEFAULT_DOMAIN'),
                    ],
        
                    'storage' => [
                        'default_bucket' => env('FIREBASE_STORAGE_DEFAULT_BUCKET'),
                    ],
                    'cache_store' => env('FIREBASE_CACHE_STORE', 'file'),
                    'logging' => [
                        'http_log_channel' => env('FIREBASE_HTTP_LOG_CHANNEL'),
                        'http_debug_log_channel' => env('FIREBASE_HTTP_DEBUG_LOG_CHANNEL'),
                    ],
                    'http_client_options' => [
                        'timeout' => env('FIREBASE_HTTP_CLIENT_TIMEOUT'),
                    ],
                ]
          ]
];

Error message/Stack trace

Throwable Class:	NotificationChannels\Fcm\Exceptions\CouldNotSendNotification
Throwable Message:	SenderId mismatch

#0 /app/vendor/laravel-notification-channels/fcm/src/FcmChannel.php(84): NotificationChannels\Fcm\Exceptions\CouldNotSendNotification::serviceRespondedWithAnError(Object(Kreait\Firebase\Exception\Messaging\AuthenticationError))
kreait/firebase-php#1 /app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationSender.php(148): NotificationChannels\Fcm\FcmChannel->send(Object(App\User), Object(App\Notifications\FirebasePush))
kreait/firebase-php#2 /app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationSender.php(106): Illuminate\Notifications\NotificationSender->sendToNotifiable(Object(App\User), '4115cac3-e95b-4...', Object(App\Notifications\FirebasePush), 'NotificationCha...')
kreait/firebase-php#3 /app/vendor/laravel/framework/src/Illuminate/Support/Traits/Localizable.php(19): Illuminate\Notifications\NotificationSender->Illuminate\Notifications\{closure}()

Additional information

Application is hosted for multiple domains using https://github.com/gecche/laravel-multidomain

Thank you for providing details for the issue, much appreciated (many don't do it 😅).

When you commented to that other issue, I didn't know that this problem with occurred in the context of/while using the FCM notification channel package.

Unfortunately, I'm not familiar with the package and it's not in scope of my package. Using multiple projects "vanilla-style" with my package should work, but I don't know why it doesn't with the other package. I believe you should open an issue on their package instead.

What I did notice is that you're using the same FIREBASE_CREDENTIALS and other environment variable for both project configurations, could that be the reason?

@jeromegamez Thank you for taking time to look into it.

As I have multiple domains using same codebase, each domain use dedicated .env file.
For Example: If I've configured www.example.com so the env file would be .env.www.example.com.

So, I've different domains using different firebase projects.
I did tried logging the firebase config values to the log to see whether it is has the correct values. Unfortunately, I'm seeing correct values.

As of now, I'm suspecting the service provider, as it is using the singleton instance. However, I do not have enough info to support my hypothesis.

$this->app->singleton(Firebase\Contract\Messaging::class, static fn (Container $app) => $app->make(FirebaseProjectManager::class)->project()->messaging());

Thank you for the additional insights!

You're right - if you type-hint the Messaging interface, it will (should) inject the messaging component of the default project defined in the firebase.default config.

Just to be sure, are you setting the FIREBASE_PROJECT environment variable differently on each host, in your example to myprojectOne?

Just to be sure, are you setting the FIREBASE_PROJECT environment variable differently on each host, in your example to myprojectOne?

Yes, Correct. It is different.

Firebase has app limit between 30-50. So, to avoid the limit. I've different projects set for different host.

To be clear. .env.example.com may have FIREBASE_PROJECT= myprojectOne and .env.newhost.com may have FIREBASE_PROJECT= myproject.

@jeromegamez Any suggestion on this?

I just tested this in a fresh Laravel 8 project with version 4.2 of this package (please note, I don't support versions older than the current one, but I haven't added a supported versions matrix to the README yet, so here we are 😅)

To make my manual tests simple, I have this config/firebase.php file - in order to test that the correct project is picked up, we can get a database reference and show its URL (at this point, no request is executed, but we can see if the correct FIREBASE_DATABASE_URL is picked up.

I noticed that, in the config you provided, the database URL is

<?php

return [
    'default' => env('FIREBASE_PROJECT', 'myproject'),
    'projects' => [
        'myproject' => [
                    'credentials' => [
                        'file' => env('FIREBASE_CREDENTIALS', 'config/myproject.json'),
                    ],
                    'database' => [
                        'url' => env('FIREBASE_DATABASE_URL', 'https://myproject.firebaseio.com'),
                    ],
                ],
        'myprojectOne' => [
                    'credentials' => [
                        'file' => env('FIREBASE_CREDENTIALS', 'config/myprojectOne.json'),
                    ],
                    'database' => [
                        'url' => env('FIREBASE_DATABASE_URL', 'https://myprojectOne.firebaseio.com'),
                    ],
                ]
    ],
];

(Note that I have changed the FIREBASE_DATABASE_URL for myprojectOne - in your config, both have the same value.)

In a tinker session, we can now test with

> app('firebase.manager')->project()->database()->getReference()->getUri()->__toString()
= "https://myproject.firebaseio.com/"

if the expected Database URI is printed out.

In my tests, it worked with the following setups:

  • FIREBASE_PROJECT=myproject " ✅
  • FIREBASE_PROJECT=myprojectOne " ✅
  • with FIREBASE_PROJECT not defifined -> myproject is used ✅

Here are my suggestions:

  • Test the same in your project (remember to change the database URL in projectOne), either in a Tinker session or by dump()/dd()ing it somewhere
  • Replace 'default' => env('FIREBASE_PROJECT', 'myproject'), with 'default' => env('FIREBASE_PROJECT), - with this, you will get an error if the FIREBASE_PROJECT environment variable is not set.

Long story short: I can't reproduce the problem and must assume that this is caused by something else, probably in the usage of the FCM Notification Channel package.

I mentioned earlier that versions < 5.x are not supported anymore - the laravel-notification-channels/fcm package has also a newer major release (3.x). Perhaps there is something that doesn't add up. (PHP 8.0.* is EOLed as well)

I hope this helps.

Replace 'default' => env('FIREBASE_PROJECT', 'myproject'), with 'default' => env('FIREBASE_PROJECT), - with this, you will get an error if the FIREBASE_PROJECT environment variable is not set.

This did the trick. It was always reading the default value instead of the env value. Not sure why.

Thank you for your help!