Filament Firebase Auth
Firebase Auth-login voor Filament 5-panels via de auth-contracts van Laravel — met dezelfde gebruikers en wachtwoorden als je andere Firebase-apps.

Wat het is
Een Filament 5-plugin die gebruikers via Firebase Authentication laat inloggen in Filament-panels.
De plugin werkt via de eigen auth-contracts van Laravel: een firebase user provider, een firebase password broker en een model-trait.
Daardoor blijven de login-, wachtwoordreset- en e-mailverificatiepagina's van Filament, guards, policies, canAccessPanel(), rate limits en events gewoon werken.
Waarvoor het dient
Je gebruikers bestaan al in een Firebase-project, bijvoorbeeld voor een mobiele app of een andere webapp. Ze moeten in je Filament-panels inloggen met hetzelfde e-mailadres en wachtwoord, of met hetzelfde Google-account.
Met deze plugin blijft Firebase de enige plek die de wachtwoorden kent. De Laravel-database bewaart geen wachtwoord en geen wachtwoordhash. De plugin voegt één koppeltabel toe die je lokale records met Firebase-gebruikers verbindt.
Functies
Inloggen met e-mailadres en wachtwoord op de eigen loginpagina van Filament.
Inloggen met social providers (Google, Apple, Microsoft, GitHub, Facebook, X (Twitter), Yahoo) via de Firebase JS SDK.
Registratie: de plugin maakt eerst het Firebase-account aan en daarna het lokale record.
Wachtwoordreset op de eigen pagina's van Filament. Firebase krijgt het nieuwe wachtwoord.
E-mailverificatie op de eigen pagina's van Filament. Je andere Firebase-apps delen dezelfde verified-vlag.
Profielpagina: wijziging van naam, e-mailadres en wachtwoord in Firebase, "log out other sessions", optioneel "log out everywhere" en verwijdering van het account.
De multifactorauthenticatie van Filament voor inloggen met e-mailadres en wachtwoord.
Eén model, guard en set regels per panel. Elk panel bepaalt of een login een lokaal record aanmaakt ("open" of "gesloten" panel).
make:filament-usermaakt het Firebase-account en het lokale record aan.Een sessiecontrole op de achtergrond logt een sessie uit wanneer het Firebase-account verwijderd of uitgeschakeld is, of wanneer de tokens ervan ingetrokken zijn.
Accountverwijdering in Firebase is opt-in, met een veilige standaardwaarde (
never).Uitbreidingspunten als interfaces. Voor elk ervan kun je je eigen klasse binden.
Wanneer je het niet gebruikt
Je hebt token-auth voor een API nodig (Sanctum, bearer ID-tokens). De plugin is alleen voor panelsessies.
Je gebruikt Firebase multi-tenancy (
tenantId), inloggen via telefoon of anoniem inloggen.Je gebruikt Filament 3 of Filament 4. De plugin ondersteunt alleen Filament 5.
Lees Beperkingen voor de volledige lijst.
Hoe het werkt
De
firebaseuser provider controleert het e-mailadres en het wachtwoord bij Firebase. De loginpagina van Filament roept hem aan via hetUserProvider-contract van Laravel.Nadat Firebase de inloggegevens accepteert, zoekt de plugin het lokale record van het panelmodel of maakt het aan. Daarna koppelt de plugin het record aan de Firebase-UID in de tabel
firebase_identities.Social-knoppen loggen in met een Firebase-pop-up in de browser. De server controleert het Firebase ID-token en voert daarna dezelfde opzoeking uit.
De sessie bewaart een fingerprint van de Firebase-status in plaats van een wachtwoordhash.
AuthenticateSessionvan Laravel logt de sessie uit wanneer die status verandert.Wachtwoordreset, verificatie en profielwijzigingen gebruiken de pagina's van Filament en schrijven het resultaat naar Firebase.
Lees de architectuurgids voor de details.
Vereisten
PHP 8.3 of hoger
Laravel 12.69.3 of hoger, of Laravel 13.33 of hoger
Filament 5.8.3 of hoger (Composer blokkeert oudere Filament 5-releases vanwege beveiligingsadviezen)
Een Firebase-project met een serviceaccount
kreait/laravel-firebase7.2 of hoger (Composer installeert het samen met de plugin)
Versiebeheer
De major-versie van de plugin is gelijk aan de major-versie van Filament: plugin 5.x werkt met Filament 5.x.
Binnen één major volgen releases semantic versioning.
Lees RELEASING.md voor de regels en UPGRADING.md voor upgrades tussen majors.
Installatie
Installeer het package:
composer require isapp/filament-firebase-auth
Publiceer het configbestand en de migratie van de koppeltabel:
php artisan filament-firebase-auth:install
Controleer het sleuteltype van je modellen voordat je de migratie uitvoert. De koppeltabel gebruikt de morphs()-kolom van Laravel.
Ze volgt Schema::morphUsingUuids() en Schema::morphUsingUlids().
Gebruiken je panelmodellen UUID- of ULID-sleutels, roep dan een van deze methoden aan in een service provider, of verander morphs() in uuidMorphs() of ulidMorphs() in de gepubliceerde migratie.
php artisan migrate
Het gebouwde JavaScript van de social-knoppen zit in resources/dist. Je hebt geen npm nodig.
Publiceer de Filament-assets zoals gewoonlijk, bijvoorbeeld na elke update:
php artisan filament:assets
Firebase instellen
Deze sectie brengt je van nul naar een Firebase-project dat de plugin kan gebruiken. De Firebase console verandert af en toe zijn menu's. Nieuwere consoles zetten Authentication onder Security. Wijkt een label af, zoek dan naar dezelfde woorden in de buurt.
1. Maak een Firebase-project aan of kies er een
Een andere app gebruikt al een Firebase-project (bijvoorbeeld je mobiele app): gebruik dat project. Gedeelde gebruikers zijn de reden voor deze plugin. Laat
delete_accountsin dat geval opneverstaan. Lees Accountverwijdering.Nog geen project: maak in de Firebase console een nieuw project aan, vul een projectnaam in en bevestig. Google Analytics is niet nodig voor deze plugin.
Firebase-handleiding: Add Firebase to your JavaScript project.
2. Schakel de login-providers in
Open Authentication → Sign-in method.
Email/Password: schakel het in en sla op. Inloggen met e-mailadres en wachtwoord in de plugin heeft het nodig. Firebase-handleiding.
Schakel elke social provider in die je in
providers()opgeeft. Elke provider behalve Google heeft een app nodig in de developer console van die provider. Voor de meeste providers toont het Firebase-dialoogvenster een OAuth redirect URI in de vormhttps://YOUR-PROJECT-ID.firebaseapp.com/__/auth/handler. Registreer deze URI (of hetfirebaseapp.com-domein ervan) in de app van de provider.
| Provider | Wat Firebase vraagt | Firebase-handleiding |
|---|---|---|
| Inschakelen en opslaan. De console kan om een support-e-mailadres voor het project vragen. Er is geen andere app nodig. | ||
| Apple | Een lidmaatschap van het Apple Developer Program: een Services ID, je Team ID en een private key voor "Sign in with Apple" met de bijbehorende key ID. Registreer de redirect URI bij Apple als Return URL. | Apple |
| Microsoft | Een app-registratie in Microsoft Entra ID (voorheen Azure Active Directory): de client ID en een client secret ervan. Registreer daar de redirect URI. Gebruik providerParameters(SocialProvider::Microsoft, ['tenant' => '...']) om maar één tenant toe te laten. | Microsoft |
| GitHub | Een GitHub OAuth-app: de client ID en client secret ervan. Zet de redirect URI in "Authorization callback URL". | GitHub |
| Een app in Facebook for Developers: de App ID en App Secret ervan. Zet de redirect URI in de Facebook Login-instellingen van de app, in de lijst met OAuth redirect URI's. | ||
| X (Twitter) | Een X developer-app: de API key en API secret ervan. Zet de redirect URI in de callback-URL van de app. | |
| Yahoo | Een Yahoo developer-app met de OpenID Connect-rechten profile en email: de client ID en client secret ervan. Registreer daar de redirect URI. | Yahoo |
3. Voeg je domeinen toe aan de geautoriseerde domeinen
Open Authentication → Settings → Authorized domains en klik op Add domain.
Voeg elk domein toe dat een Firebase-panel serveert, bijvoorbeeld admin.example.com.
Voeg voor lokale ontwikkeling ook localhost toe, maar alleen aan een ontwikkelproject: nieuwe projecten bevatten het standaard niet meer, en Firebase raadt het af in productie.
Zonder het domein mislukt de pop-up voor social login met auth/unauthorized-domain.
Firebase-handleiding: de opmerking "Authorized domains" in Email link authentication.
4. Maak de servercredentials aan (serviceaccount)
De server communiceert met Firebase via kreait/laravel-firebase en gebruikt daarvoor een serviceaccount.
Open Project settings → Service accounts.
Klik op Generate new private key en daarna op Generate key. De browser downloadt een JSON-bestand.
Verplaats het bestand naar je app, buiten de public-directory, bijvoorbeeld
storage/app/firebase/service-account.json.Voeg het bestand toe aan
.gitignore. Commit het nooit. Iedereen met dit bestand kan elke gebruiker van het project beheren.
/storage/app/firebase/service-account.json
Laat
kreait/laravel-firebaseer in.envnaar verwijzen. Een relatief pad begint bij de root van je app:
FIREBASE_CREDENTIALS=storage/app/firebase/service-account.json
kreait/laravel-firebase leest ook GOOGLE_APPLICATION_CREDENTIALS wanneer FIREBASE_CREDENTIALS leeg is.
Geef in productie elke server een eigen kopie van het bestand, bijvoorbeeld via je secret store of deploytool.
Je hebt het configbestand van kreait/laravel-firebase alleen nodig als je meer wilt wijzigen dan de credentials (bijvoorbeeld de HTTP-timeout). Zo publiceer je het:
php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" --tag=config
Laat FIREBASE_AUTH_TENANT_ID leeg. De plugin ondersteunt geen Firebase multi-tenancy.
Firebase-handleiding: Add the Firebase Admin SDK to your server.
5. Registreer een webapp voor de social-knoppen
De social-knoppen draaien de Firebase JS SDK in de browser. Die SDK heeft de config van een Firebase-webapp nodig. Je kunt deze stap overslaan als het panel geen social providers heeft.
Klik op de overzichtspagina van het project op het Web-icoon (
</>). Heeft het project al apps, klik dan op Add app en daarna op Web.Vul een bijnaam in en klik op Register app. De plugin heeft Firebase Hosting niet nodig.
Kopieer de waarden van het
firebaseConfig-object naar.env:
firebaseConfig-sleutel | Env-sleutel van de plugin |
|---|---|
apiKey | FIREBASE_WEB_API_KEY |
authDomain | FIREBASE_WEB_AUTH_DOMAIN |
projectId | FIREBASE_WEB_PROJECT_ID |
appId | FIREBASE_WEB_APP_ID |
FIREBASE_WEB_API_KEY=AIza...
FIREBASE_WEB_AUTH_DOMAIN=your-project-id.firebaseapp.com
FIREBASE_WEB_PROJECT_ID=your-project-id
FIREBASE_WEB_APP_ID=1:1234567890:web:abc123
Dezelfde waarden vind je later onder Project settings → General → Your apps → Config.
De web-apiKey is geen geheim. Hij identificeert alleen je Firebase-project, en elke bezoeker van de loginpagina kan hem zien.
Beperk hem toch: open in de Google Cloud console APIs & Services → Credentials en bewerk de sleutel.
Sta onder Application restrictions alleen je paneldomeinen en je authDomain (your-project-id.firebaseapp.com) toe als HTTP-referrers. De login-pop-up draait op het authDomain en gebruikt de sleutel ook. Sta onder API restrictions alleen de API's toe die Firebase Authentication nodig heeft (Identity Toolkit API en Token Service API).
Test de wijziging eerst op een testsleutel, want een foute beperking breekt het inloggen.
Op een gedeeld Firebase-project gebruiken de webapps van het project vaak dezelfde browsersleutel.
Een beperking voor alleen het panel breekt dan de andere apps: hun domeinen zijn niet toegestaan, en de API's die ze nodig hebben, zijn geblokkeerd.
Neem dus ofwel de domeinen en de API's van alle apps die de sleutel gebruiken op in de beperking,
ofwel maak een aparte browsersleutel voor het panel (APIs & Services → Credentials → Create credentials → API key), zet die in FIREBASE_WEB_API_KEY en beperk alleen die sleutel.
Lees Learn about using and managing API keys for Firebase.
6. Aanbevolen instellingen
Email enumeration protection: open Authentication → Settings → User actions. Firebase zet deze bescherming standaard aan voor projecten die op of na 15 september 2023 zijn aangemaakt. De plugin werkt met de bescherming aan of uit, en toont in beide gevallen dezelfde foutmelding bij elke mislukte login.
Password policy: open Authentication → Settings → Password policy om een minimale lengte of tekentypes te vereisen. De registratie- en profielformulieren van Filament controleren
Password::defaults()van Laravel. Stel daar dezelfde regels in, zodat de formulieren ze ook controleren. Op een gedeeld project geldt het beleid voor alle apps. Laat je het beleid bij het inloggen een update van niet-conforme wachtwoorden vereisen, dan kunnen bestaande gebruikers van de andere apps geblokkeerd raken tot ze hun wachtwoord wijzigen.Email templates: de plugin gebruikt de e-mailtemplates van Firebase niet. E-mails voor wachtwoordreset, e-mailverificatie en e-mailwijziging komen van Filament en Laravel. Configureer daarvoor Laravel mail (
MAIL_MAILERen de rest).
Firebase-handleiding: wachtwoordbeleid en bescherming tegen e-mailenumeratie.
7. Lokale ontwikkeling met de Firebase Auth Emulator
Je kunt ontwikkelen zonder een echt Firebase-project. De Firebase Auth Emulator heeft Node en Java 21 of hoger nodig.
npm install --save-dev --save-exact firebase-tools
npx --no-install firebase-tools emulators:start --only auth --project demo-your-app
Zonder firebase.json luistert de Auth-emulator op zijn standaardpoort 9099.
Om host en poort vast te leggen, voeg je een firebase.json aan je app toe, zoals de plugin dat doet:
{
"emulators": {
"auth": { "host": "127.0.0.1", "port": 9099 }
}
}
Zet daarna in .env:
FIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099
FIREBASE_CREDENTIALS='{"type":"service_account","project_id":"demo-your-app","client_email":"emulator@demo-your-app.iam.gserviceaccount.com","private_key":"unused"}'
kreait/firebase-php stuurt dan alle Auth-calls naar de emulator. Een project-ID dat met demo- begint, heeft geen echt Firebase-project nodig.
De emulator negeert credentials. De placeholder geeft kreait/firebase-php alleen de project-ID.
Zonder placeholder zoekt kreait/firebase-php naar Google-credentials op je machine: een trage probe van de cloud-metadataserver, of je echte credentials.
Inloggen met e-mailadres en wachtwoord, registratie, wachtwoordreset, verificatie, de profielpagina en make:filament-user werken met de emulator.
De social-knoppen niet: test die met een echt ontwikkelproject.
De emulator houdt zijn accounts in het geheugen. Na een herstart zijn ze weg.
kreait/firebase-php leest FIREBASE_AUTH_EMULATOR_HOST uit de procesomgeving, niet uit de Laravel-config.
Na php artisan config:cache laadt Laravel .env niet meer, dus de waarde in .env heeft geen effect.
Zet de variabele dan in de echte omgeving van het proces (shell, Docker, procesmanager), of cache de config niet bij lokale ontwikkeling.
Let op: zet
FIREBASE_AUTH_EMULATOR_HOSTnooit in productie.Met deze variabele controleertkreait/firebase-phpde handtekening van ID-tokens niet. Iedereen kan dan een vervalst token naar de route voor social login posten en als elke willekeurige gebruiker inloggen.
8. Firebase-probleemoplossing
| Symptoom | Oorzaak en oplossing |
|---|---|
| De social-knop toont "The browser blocked the sign-in window." | De browser heeft de pop-up geblokkeerd (auth/popup-blocked). Sta pop-ups toe voor je site en klik opnieuw. |
| De social-knop toont meteen "The sign-in failed. Try again." | Vaak auth/unauthorized-domain: voeg het domein toe aan de geautoriseerde domeinen (stap 3). Of auth/operation-not-allowed: schakel de provider in onder Sign-in method (stap 2). Of de FIREBASE_WEB_*-waarden kloppen niet (stap 5). |
| "Unable to determine the Firebase Project ID" | kreait/firebase-php heeft geen credentials gevonden. Controleer FIREBASE_CREDENTIALS en het bestandspad (stap 4). Gebruik bij de emulator de placeholder-credentials (stap 7). Voer na een wijziging van .env het commando php artisan config:clear uit. |
Een fout terwijl kreait/firebase-php de credentials leest | Het bestand is niet de JSON van het serviceaccount, of het is onvolledig. Genereer een nieuwe sleutel (stap 4). Gebruik niet de webconfig of een google-services.json-bestand. |
| Elke login met wachtwoord mislukt, en de log zegt dat Firebase faalde | Het serviceaccount is fout of verwijderd, of de server kan Firebase niet bereiken. De plugin logt alleen de exceptionklasse. Controleer stap 4 en het netwerk. |
| De social-knop toont "The page has expired. Reload the page and try again." | Het CSRF-token van de pagina is verlopen (HTTP 419), bijvoorbeeld na lange inactiviteit. Herlaad de loginpagina. |
Configureer de auth-guard, provider en password broker
Elk Firebase-panel gebruikt een eigen guard. De guard gebruikt een user provider met de firebase-driver.
De password broker van het panel gebruikt ook de firebase-driver.
// config/auth.php
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'firebase_admins',
],
],
'providers' => [
'firebase_admins' => [
'driver' => 'firebase',
'model' => App\Models\Admin::class,
],
],
'passwords' => [
'admins' => [
'driver' => 'firebase',
'provider' => 'firebase_admins',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
De firebase-broker bewaart zijn tokens in een databasetabel, dus table is verplicht.
De standaardtabel password_reset_tokens van Laravel werkt.
Configureer het panel
Login, wachtwoordreset en e-mailverificatie zijn de eigen pagina's van Filament. Je zet ze aan zoals gewoonlijk.
Registratie en profiel hebben de pagina's van de plugin nodig, omdat de pagina's van Filament een lokaal wachtwoord zouden opslaan.
Gebruik voor multifactorauthenticatie de AppAuthentication van de plugin en de required-MFA-middleware van de plugin.
use Filament\Panel;
use Isapp\FilamentFirebaseAuth\Enums\SocialProvider;
use Isapp\FilamentFirebaseAuth\FilamentFirebaseAuthPlugin;
use Isapp\FilamentFirebaseAuth\Http\Middleware\EnsureMultiFactorAuthenticationIsEnabled;
use Isapp\FilamentFirebaseAuth\MultiFactor\AppAuthentication;
use Isapp\FilamentFirebaseAuth\Pages;
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
->path('admin')
->authGuard('admin')
->authPasswordBroker('admins')
->login()
->registration(Pages\Register::class)
->passwordReset()
->emailVerification()
->emailChangeVerification()
->profile(Pages\EditProfile::class)
->multiFactorAuthentication([
AppAuthentication::make()->recoverable(),
])
->multiFactorAuthenticationRequiredMiddlewareName(EnsureMultiFactorAuthenticationIsEnabled::class)
// ->middleware([...]), ->authMiddleware([...]) and the rest as usual.
->plugin(
FilamentFirebaseAuthPlugin::make()
->createOnSignIn(false)
->providers([SocialProvider::Google, SocialProvider::Microsoft])
->providerParameters(SocialProvider::Microsoft, ['tenant' => 'your-tenant-id']),
);
}
Roep ->id() aan vóór ->plugin(). De plugin leest de panel-middleware wanneer je de plugin toevoegt.
Bij elke panelrequest controleert de plugin deze setup. De plugin controleert elk panel waarvan de guard de firebase-driver gebruikt, ook een panel zonder de plugin.
De plugin gooit een LogicException wanneer zo'n panel:
de eigen registratiepagina, profielpagina, app-authenticatie of required-MFA-middleware van Filament gebruikt,
->passwordReset()heeft, maar de password broker ervan niet defirebase-driver gebruikt (zonder->authPasswordBroker()gebruikt Filament de standaardbroker uitconfig/auth.php),een model heeft zonder de
HasFirebaseIdentity-trait.
De melding noemt de oplossing. De plugin slaat nooit stilzwijgend een lokaal wachtwoord op.
Houd ->emailChangeVerification() aan op een panel met de profielpagina. Zonder deze instelling verandert een e-mailwijziging in het profiel het gedeelde Firebase-e-mailadres meteen, zonder bewijs dat de persoon eigenaar is van het nieuwe adres.
Wachtwoordresets in alle apps op het project gaan dan naar het nieuwe adres.
Plugin-opties
| Methode | Standaard | Wat het doet |
|---|---|---|
createOnSignIn(bool | Closure) | true | Bij true maakt een login met een Firebase-account zonder lokaal record dat record aan ("open" panel). Bij false kunnen alleen gekoppelde of koppelbare records inloggen ("gesloten" panel). |
providers(array) | [] | De SocialProvider-cases die een knop krijgen op de login- en registratiepagina's, in deze volgorde. Geen providers: geen knoppen en geen login-route. |
providerScopes(SocialProvider, array) | [] | Extra OAuth-scopes voor één provider. Apple vraagt altijd om email en name. |
providerParameters(SocialProvider, array) | [] | Eigen OAuth-parameters voor één provider, bijvoorbeeld ['tenant' => '...'] voor Microsoft of ['prompt' => 'select_account'] voor Google. |
createOnSignIn() regelt de registratiepagina niet. Registratie is de eigen ->registration()-instelling van Filament.
Stel de providers in de panelconfiguratie in, niet later tijdens runtime.
De plugin voegt de login-route alleen toe aan een panel dat providers heeft op het moment dat Laravel de routes laadt (ook met php artisan route:cache).
Bereid je users-tabel voor
De plugin slaat nooit een wachtwoord op. Je tabel mag de password-kolom houden, maar de kolom moet null accepteren.
Anders mislukt de eerste login van een nieuwe gebruiker met een melding die naar deze sectie verwijst.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('password')->nullable()->change();
});
}
};
Je kunt de kolom ook verwijderen als geen andere code hem gebruikt.
De plugin verwacht ook:
een
name- en eenemail-kolom in de tabel,nameenemailin de$fillable-lijst van het model (het standaard aanmaken van records gebruikt ze).
Firebase slaat e-mailadressen op in kleine letters. Bewaar lokale e-mailadressen ook in kleine letters op een database met hoofdlettergevoelige vergelijkingen (SQLite, PostgreSQL).
Anders matcht een lokaal record met Jane@example.com niet met de Firebase-gebruiker jane@example.com.
Bereid je model voor
Voeg de HasFirebaseIdentity-trait toe aan het model van elk Firebase-panel.
Het model implementeert MustVerifyEmail en CanResetPassword, en gebruikt Notifiable.
namespace App\Models;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Isapp\FilamentFirebaseAuth\Concerns\HasFirebaseIdentity;
class Admin extends Authenticatable implements CanResetPassword, FilamentUser, MustVerifyEmail
{
use HasFirebaseIdentity;
use Notifiable;
protected $fillable = [
'name',
'email',
];
public function canAccessPanel(Panel $panel): bool
{
return true; // Your own rule.
}
}
De trait leest de verified-vlag uit de koppeltabel en geeft vanuit getAuthPassword() een sessie-fingerprint terug.
markEmailAsVerified() en markEmailAsUnverified() schrijven de vlag naar Firebase, zodat de wijziging zichtbaar is in alle apps op het Firebase-project.
markEmailAsVerified() leest eerst de Firebase-gebruiker. Is het Firebase-e-mailadres niet het lokale e-mailadres dat de verificatielink heeft bewezen, dan verandert de methode niets en geeft ze false terug.
De trait verwijdert ook de koppeling wanneer het record verwijderd wordt.
Gebruikt je model de Authenticatable- of MustVerifyEmail-traits van Laravel rechtstreeks (niet via Illuminate\Foundation\Auth\User), dan meldt PHP een methodeconflict.
Zeg PHP dat het de methoden van de plugin-trait moet gebruiken:
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Isapp\FilamentFirebaseAuth\Concerns\HasFirebaseIdentity;
class Admin extends Model implements AuthenticatableContract, CanResetPasswordContract, MustVerifyEmailContract
{
use Notifiable;
use Authenticatable, CanResetPassword, MustVerifyEmail, HasFirebaseIdentity {
HasFirebaseIdentity::getAuthPassword insteadof Authenticatable;
HasFirebaseIdentity::hasVerifiedEmail insteadof MustVerifyEmail;
HasFirebaseIdentity::markEmailAsVerified insteadof MustVerifyEmail;
HasFirebaseIdentity::markEmailAsUnverified insteadof MustVerifyEmail;
HasFirebaseIdentity::sendEmailVerificationNotification insteadof MustVerifyEmail;
HasFirebaseIdentity::getEmailForVerification insteadof MustVerifyEmail;
}
}
Twee panels kunnen één model delen. Dan delen ze één record per Firebase-gebruiker. Twee panels met verschillende modellen krijgen voor dezelfde Firebase-gebruiker één record in elk model.
Hoe een login het lokale record vindt
Bij elke login (wachtwoord of social) past de plugin deze regels in volgorde toe:
Er bestaat een koppeling voor deze Firebase-UID en dit model: gebruik het record van die koppeling.
Geen koppeling, maar een record heeft hetzelfde e-mailadres: koppel dat record, maar alleen als Firebase het e-mailadres heeft geverifieerd. Iedereen kan een niet-geverifieerd Firebase-account aanmaken met het e-mailadres van iemand anders. Daarom koppelt een niet-geverifieerd e-mailadres nooit een bestaand record.
Geen koppeling, geen record, en het panel is open (
createOnSignIn(true)): maak het record aan.Anders mislukt de login.
De login mislukt ook voor een uitgeschakeld Firebase-account, voor een record dat aan een andere Firebase-UID gekoppeld is en voor een gebruiker zonder toegang tot het panel. Een Firebase-gebruiker zonder e-mailadres (bijvoorbeeld een Twitter-account zonder de e-mailtoestemming) kan ook niet inloggen, tenzij er al een koppeling bestaat. Elke mislukking toont de eigen melding "These credentials do not match our records" van Filament, zodat niemand kan achterhalen welke e-mailadressen bestaan.
Regel 2 vertrouwt de emailVerified-vlag van Firebase, ongeacht welke provider die vlag heeft gezet. Laat alleen providers waarvan je het e-mailadres vertrouwt bestaande records via e-mailadres koppelen.
De firebase-driver past extra credentials en withQuery() toe zoals de eloquent-driver van Laravel dat doet.
Als je bijvoorbeeld 'is_active' => true toevoegt aan de credentials van de loginpagina, kan een record met is_active = false niet inloggen.
Deze voorwaarden beperken een bestaand record. Een nieuw record op een open panel hoeft er niet aan te voldoen.
Social login
Voeg providers toe met de SocialProvider-enum: Google, Apple, Microsoft, GitHub, Facebook, Twitter en Yahoo.
De knoppen verschijnen via render hooks onder de eigen login- en registratieformulieren van Filament.
Het JavaScript laadt alleen op deze pagina's.
De browser logt in met een Firebase-pop-up en post het Firebase ID-token naar een route van het panel.
De server controleert het token met verifyIdToken(), ook op intrekking. Het token moet:
afkomstig zijn van een van de providers van dit panel (een wachtwoordtoken, anoniem token of custom token mislukt),
een login van de laatste 5 minuten bewijzen.
De route heeft de panel-middleware, de CSRF-controle en een rate limit van 5 pogingen per minuut per panel en IP-adres.
Social login vertrouwt de provider en de eigen tweede factor van die provider. Social login toont de multifactor-challenge van Filament niet.
Heb je een tweede factor nodig voor deze gebruikers, zet die dan aan bij de provider (bijvoorbeeld in Google Workspace of Microsoft Entra).
dutchcodingcompany/filament-socialite volgt dezelfde aanpak.
Lees de waarschuwing in Tweefactorauthenticatie: deze regel geldt ook voor gebruikers met een wachtwoord.
Tweefactorauthenticatie
De multifactorauthenticatie van Filament beschermt inloggen met e-mailadres en wachtwoord. De loginpagina van Filament voert de challenge zoals gewoonlijk uit.
Gebruik
Isapp\FilamentFirebaseAuth\MultiFactor\AppAuthenticationin plaats van deAppAuthenticationvan Filament. De dialoogvensters van deze klasse voor instellen, uitschakelen en herstelcodes controleren het wachtwoord bij Firebase. De eigen klasse van Filament vergelijkt een lokale hash, en die is er niet.Bereid het model voor op app-authenticatie zoals de "Multi-factor authentication"-docs van Filament beschrijven: implementeer
HasAppAuthentication(enHasAppAuthenticationRecoveryvoor herstelcodes), gebruik de bijbehorendeInteractsWith...-traits en voeg de kolommenapp_authentication_secret(enapp_authentication_recovery_codes) toe.De e-mailcode-authenticatie van Filament heeft geen wachtwoordstap. Ze werkt zonder pluginklasse.
Is multifactorauthenticatie verplicht, stel dan de plugin-middleware in met
->multiFactorAuthenticationRequiredMiddlewareName(EnsureMultiFactorAuthenticationIsEnabled::class). Een gebruiker die alleen met een social provider inlogt, komt er dan door. Alle andere gebruikers krijgen het gedrag van Filament.
Waarschuwing: social login slaat de multifactor-challenge van Filament over voor elke gebruiker, ook voor een gebruiker met een Firebase-wachtwoord.Iemand die het Firebase-wachtwoord van een account kent, kan rechtstreeks bij Firebase inloggen (bijvoorbeeld via de Firebase REST API), een eigen Google- of GitHub-account aan die Firebase-gebruiker koppelen en daarna de social-knop gebruiken. De plugin logt die persoon dan in zonder multifactor-challenge van Filament. De multifactorauthenticatie van Filament beschermt alleen het formulier voor e-mailadres en wachtwoord. Zo beperk je dit risico:
Voeg geen social providers (
->providers([...])) toe aan een panel dat multifactorauthenticatie vereist. Dit is de enige volledige oplossing.Houd de Firebase-standaard van één account per e-mailadres aan (Authentication → Settings → User account linking). Die standaard voorkomt een tweede Firebase-account met hetzelfde e-mailadres. De koppelstap hierboven voorkomt die standaard niet.
Laat email enumeration protection aanstaan, zodat een aanvaller de e-mailadressen van je accounts niet makkelijk kan vinden.
Vereis een tweede factor bij de provider voor de accounts die ertoe doen (bijvoorbeeld in Google Workspace of Microsoft Entra).
Gebruikers zonder Firebase-wachtwoord zien geen acties voor app-authenticatie op de profielpagina. De sectiekop kan nog steeds verschijnen, zonder inhoud.
Stel de plugin-middleware in telkens wanneer het panel multifactorauthenticatie gebruikt.
De setupcontrole leest isRequired één keer, wanneer het panel opstart. Ze kan geen closure zien die van de gebruiker afhangt.
Voor een gebruiker die alleen met een social provider inlogt, op een panel dat multifactorauthenticatie vereist, vraagt de middleware Firebase één keer per request naar de providers van de gebruiker.
Sessies
Sessiecontrole
Een andere app of de Firebase console kan een account verwijderen of uitschakelen, of de tokens ervan intrekken (Firebase doet dat bij een wachtwoordwijziging). Firebase brengt je app daarvan niet op de hoogte. Daarom controleert de plugin de Firebase-gebruiker van elke sessie op de achtergrond:
Na
revalidate_everyminuten (standaard15) start de volgende panelrequest een controle.Met
revalidate_queueopnulldraait de controle na de response in hetzelfde proces. De request wacht niet op Firebase.Stel een queuenaam in om de controle op een queue worker te draaien. De job gebruikt de standaard queue connection (
QUEUE_CONNECTION). Op desync-connection draait de job meteen, en die request wacht op Firebase.Wanneer het account ontbreekt of uitgeschakeld is, of de tokens ervan ingetrokken zijn, verandert de sessie-fingerprint.
De fingerprint verandert ook wanneer het e-mailadres geverifieerd wordt, of wanneer de Firebase-gebruiker de
password-provider verliest (bijvoorbeeld nadat de eigenaar van het e-mailadres met Google inlogt en Firebase een onbewezen wachtwoord verwijdert). Dan verliest iemand die het account met het e-mailadres van iemand anders heeft aangemaakt de sessie.De
AuthenticateSession-middleware van Laravel logt de sessie dan uit bij de volgende request, ook een "remember me"-sessie.Als Firebase onbereikbaar is, verandert er niets. Bij het volgende interval probeert de plugin het opnieuw.
De plugin voegt de AuthenticateSession-middleware van Filament aan het panel toe als het panel die nog niet heeft. Zet revalidate_every op 0 om de controle uit te zetten.
Andere sessies uitloggen
De profielpagina heeft een actie "Log out other sessions". Die actie logt alle andere sessies van dit record in deze app uit. De huidige sessie blijft ingelogd.
Overal uitloggen
Zet global_logout op true om een actie "Log out everywhere" op de profielpagina te tonen.
Die actie trekt de Firebase-refreshtokens van de gebruiker in. Dit logt de persoon ook uit in je andere apps op hetzelfde Firebase-project.
Daarom staat de actie standaard uit.
Een gebruiker die alleen met een social provider inlogt, heeft geen Firebase-wachtwoord. Voor deze gebruiker vragen "Delete account", "Log out other sessions" en "Log out everywhere" geen wachtwoord, alleen een bevestiging.
Iedereen met een gestolen sessie van zo'n gebruiker kan ze dus gebruiken. Met delete_accounts = orphaned verwijdert één klik dan het gedeelde Firebase-account.
Na een wachtwoordwijziging, een e-mailwijziging, "log out other sessions" en "log out everywhere" logt de plugin de huidige sessie opnieuw in met de nieuwe fingerprint.
Laravel vuurt hiervoor zijn Login-event af.
Accountverwijdering
De plugin verwijdert altijd de lokale koppeling wanneer een record verwijderd wordt.
De configsleutel delete_accounts bepaalt wat er met het Firebase-account gebeurt:
delete_accounts | Wat de plugin in Firebase verwijdert |
|---|---|
never (standaard) | Niets. Alleen lokale data. |
new_only | Alleen een account dat Firebase tijdens deze login heeft aangemaakt, wanneer een gesloten panel (createOnSignIn(false)) de login blokkeert. Bijvoorbeeld: een onbekende logt met Google in op een gesloten panel. |
orphaned | Elk account zonder resterende koppeling in deze app: na een blokkering op een gesloten panel, nadat de gebruiker het account op de profielpagina heeft verwijderd, of nadat code of een beheerder het record heeft verwijderd. |
Waarschuwing: gebruik
orphanedniet wanneer andere apps het Firebase-project delen.De plugin ziet alleen zijn eigen koppelingen. De plugin kan niet weten dat een mobiele app of een andere webapp het account nog gebruikt. Metorphanedverwijdert het verwijderen van een record in deze app het Firebase-account voor elke app, en verliest de persoon overal toegang. Ook een geblokkeerde login op een gesloten panel leidt tot verwijdering: wanneer iemand zonder koppeling in deze app inlogt met het juiste wachtwoord of met een social provider zoals Google (bijvoorbeeld een gebruiker van je mobiele app), verwijdertorphanedhet Firebase-account van die persoon voor alle apps. Gebruikorphanedalleen wanneer deze app de enige gebruiker van het Firebase-project is. Houd in alle andere gevallenneveraan.
Meer details:
new_onlyvergelijkt het aanmaaktijdstip en het tijdstip van de laatste login van het Firebase-account. Beide moeten binnennew_account_windowseconden (standaard60) van elkaar en van nu liggen.Het Firebase-deel draait nadat de databasetransactie is gecommit. Een Firebase-fout stopt de lokale verwijdering nooit. De plugin logt de UID en de exceptionklasse.
Een soft delete houdt de koppeling. Een force delete verwijdert de koppeling.
Bulkverwijderingen via de query builder (
Admin::query()->where(...)->delete()) slaan model-events over. Ze verwijderen noch de koppeling, noch het Firebase-account. De koppeling die blijft, blokkeert die Firebase-UID in dit model. Verwijder records één voor één, of verwijder de koppelrijen zelf.Wanneer de registratie mislukt nadat de plugin een Firebase-account heeft aangemaakt, verwijdert de plugin dat account in elke modus, omdat de plugin het in dezelfde request heeft aangemaakt.
Gebruikers aanmaken met make:filament-user
De plugin vervangt het make:filament-user-commando van Filament (en de aliassen ervan). De opties blijven hetzelfde:
php artisan make:filament-user --panel=admin --name="Jane Doe" --email=jane@example.com
Het commando vraagt om het wachtwoord. Geef --password niet mee op de command line: de shellgeschiedenis bewaart het.
Op een panel waarvan de guard de firebase-driver gebruikt:
Geen Firebase-account heeft dit e-mailadres: het commando maakt er een aan met een geverifieerd e-mailadres, daarna het lokale record en de koppeling.
Er bestaat een Firebase-account met dit e-mailadres: het commando behoudt het wachtwoord ervan en koppelt het account. Het commando weigert een uitgeschakeld account, een niet-geverifieerd e-mailadres en een account dat een ander record van het panelmodel al gebruikt.
Wanneer de lokale stap mislukt, verwijdert het commando het Firebase-account alleen als het commando dat account zelf heeft aangemaakt.
Op elk ander panel voert het commando het normale gedrag van Filament uit.
Uitbreidingspunten
Bind je eigen klasse in de register()-methode van een service provider. De plugin bindt zijn standaardklassen alleen wanneer er nog niets anders gebonden is.
| Contract | Standaard | Wanneer het draait |
|---|---|---|
Isapp\FilamentFirebaseAuth\Contracts\CreatesLocalUsers | Actions\LocalUsers\CreateLocalUser | Een login, registratie of make:filament-user heeft een nieuw lokaal record nodig. |
Isapp\FilamentFirebaseAuth\Contracts\SyncsLocalUsers | Actions\LocalUsers\SyncLocalUser | Bij elke login en elke sessiecontrole. Kopieert de naam en het e-mailadres uit Firebase. |
Isapp\FilamentFirebaseAuth\Contracts\DecidesAccountDeletion | Deletion\AccountDeletionPolicy | De plugin kan een Firebase-account verwijderen (zie Accountverwijdering). |
Voorbeeld: geef elke nieuwe admin een standaardrol.
namespace App\Actions;
use Filament\Panel;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Isapp\FilamentFirebaseAuth\Contracts\CreatesLocalUsers;
use Kreait\Firebase\Auth\UserRecord;
class CreateAdmin implements CreatesLocalUsers
{
public function handle(UserRecord $firebaseUser, string $model, ?Panel $panel): Model & Authenticatable
{
return $model::query()->create([
'name' => $firebaseUser->displayName ?? $firebaseUser->email,
'email' => $firebaseUser->email,
'role' => 'viewer',
]);
}
}
create() vult alleen de attributen in de $fillable-lijst van het model. Voeg role daar dus toe, of gebruik forceCreate().
// app/Providers/AppServiceProvider.php
use App\Actions\CreateAdmin;
use Isapp\FilamentFirebaseAuth\Contracts\CreatesLocalUsers;
public function register(): void
{
$this->app->bind(CreatesLocalUsers::class, CreateAdmin::class);
}
De klasse krijgt alleen de Firebase-gebruiker. Extra velden van het registratieformulier bereiken de klasse niet.
Ook de output van mutateFormDataBeforeRegister() bereikt het record niet: de pluginpagina bouwt het record op uit de Firebase-gebruiker.
Om extra formuliervelden of gemuteerde data op te slaan, maak je een subklasse van de Pages\Register-pagina van de plugin en gebruik je de afterRegister()-hook van Filament.
Een eigen SyncsLocalUsers moet ook de koppelkolommen bijwerken, anders werkt de sessiecontrole niet meer.
De eenvoudigste manier is de standaardklasse uit te breiden:
namespace App\Actions;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Isapp\FilamentFirebaseAuth\Actions\LocalUsers\SyncLocalUser;
use Kreait\Firebase\Auth\UserRecord;
class SyncAdmin extends SyncLocalUser
{
public function handle(Model & Authenticatable $user, UserRecord $firebaseUser): void
{
parent::handle($user, $firebaseUser);
$user->forceFill(['avatar_url' => $firebaseUser->photoUrl])->save();
}
}
Schrijf je de klasse helemaal zelf, injecteer dan Isapp\FilamentFirebaseAuth\Actions\Identity\RefreshFirebaseIdentity en roep de handle()-methode ervan aan met de koppeling van het record.
Een eigen DecidesAccountDeletion draait alleen wanneer delete_accounts niet never is. In de modus never raadpleegt de plugin deze klasse nooit.
Al het andere gedrag zit in action-klassen met één doel in Isapp\FilamentFirebaseAuth\Actions\..., elk met één handle()-methode.
Je kunt ze in je eigen code injecteren, bijvoorbeeld Actions\Firebase\FindOrCreateFirebaseUser en Actions\Identity\LinkFirebaseIdentity om gebruikers aan te maken vanuit een Filament-resource.
Configuratie
config/filament-firebase-auth.php:
| Sleutel | Standaard | Betekenis |
|---|---|---|
table | firebase_identities | De koppeltabel. |
delete_accounts | never | never, new_only of orphaned. Lees Accountverwijdering. |
new_account_window | 60 | Seconden. Voor new_only telt een account dat jonger is dan deze waarde als nieuw. |
revalidate_every | 15 | Minuten tussen sessiecontroles. 0 zet de controle uit. |
revalidate_queue | null | Queue van de job voor de sessiecontrole, op de standaard queue connection. null draait de job na de response. |
global_logout | false | Toont "Log out everywhere" op de profielpagina. |
social_sign_in | js_sdk | Hoe social login werkt. js_sdk is de enige waarde. |
web.api_key, web.auth_domain, web.project_id, web.app_id | env | De Firebase-webconfig voor de social-knoppen. |
De servercredentials blijven in de config van kreait/laravel-firebase (config/firebase.php).
Probleemoplossing
"If you cannot sign in, reset your password."De plugin koppelt een bestaand lokaal record alleen aan een Firebase-account wanneer Firebase het e-mailadres heeft geverifieerd. Een wachtwoordreset bewijst dat de persoon eigenaar is van het e-mailadres. Daarom markeert de reset het e-mailadres als geverifieerd. De volgende login koppelt dan het record.
De pop-up voor social login is geblokkeerd of sluit meteen.Lees Firebase-probleemoplossing.
"This email already has an account with another sign-in method."Standaard staat Firebase één account per e-mailadres toe. De persoon heeft het account met een andere methode aangemaakt, bijvoorbeeld met een wachtwoord. Log in met die methode. De plugin koppelt geen providers aan een bestaand Firebase-account.
De eerste login van een nieuwe gebruiker mislukt, en de log noemt een NOT NULL-kolom password.Lees Bereid je users-tabel voor.
Een LogicException noemt de registratiepagina, profielpagina, app-authenticatie of middleware van Filament.Gebruik de pluginklasse die de melding noemt. Lees Configureer het panel.
Een link voor wachtwoordreset mislukt.De reset zoekt het record op e-mailadres. Wanneer een andere app het Firebase-e-mailadres heeft gewijzigd, volgt het lokale e-mailadres bij de volgende login of sessiecontrole. Tot dan kan de reset mislukken. Gebruikers die alleen met een social provider inloggen, krijgen geen reset-e-mail, omdat ze geen Firebase-wachtwoord hebben.
Inloggen mislukt, en de log zegt dat Firebase faalde.De plugin toont de normale foutmelding wanneer Firebase onbereikbaar is. De plugin logt alleen de exceptionklasse, nooit het wachtwoord of het token. Controleer het serviceaccount en het netwerk.
Je app testen met de Firebase Auth Emulator
kreait/firebase-php stuurt zijn Auth-calls naar de Firebase Auth Emulator wanneer FIREBASE_AUTH_EMULATOR_HOST gezet is.
Alle server-side Firebase-calls van de plugin gaan via die library, dus je feature tests hebben geen echt Firebase-project nodig.
De emulator heeft Node en Java 21 of hoger nodig. Pin firebase-tools als dev dependency van je app, zodat elke machine dezelfde versie draait:
npm install --save-dev --save-exact firebase-tools
npm ci
npx --no-install firebase-tools emulators:exec --project demo-your-app --only auth "php artisan test"
emulators:exec start de emulator, zet FIREBASE_AUTH_EMULATOR_HOST voor het commando en stopt de emulator daarna.
Een project-ID dat met demo- begint, heeft geen echt Firebase-project nodig.
De emulator negeert credentials, maar kreait/firebase-php heeft nog steeds een project-ID nodig.
Zonder credentials zoekt de library naar Application Default Credentials: een trage probe van de cloud-metadataserver, of de echte credentials van je machine.
Geef de library in de testomgeving placeholder-credentials voor een serviceaccount, zoals de eigen emulatortests van de plugin doen.
De private key wordt nooit gebruikt.
// For example in the setUp() of your emulator test case
config()->set('firebase.projects.app.credentials', [
'type' => 'service_account',
'project_id' => 'demo-your-app',
'client_email' => 'emulator@demo-your-app.iam.gserviceaccount.com',
'private_key' => 'unused',
]);
De social-knoppen in de browser maken geen verbinding met de emulator.
De unit- en feature tests van de plugin mocken Firebase. Een emulator-testgroep draait tegen de Firebase Auth Emulator (composer test:emulator).
Lees CONTRIBUTING.md om ze te draaien.
Beperkingen
Geen passkeys. Firebase Authentication heeft nog geen login met passkeys.
Geen Firebase-multifactorauthenticatie. De eigen multifactorauthenticatie van Filament werkt wel. Een account met een tweede factor in Firebase (SMS of TOTP, ingesteld in een andere app) kan niet met een wachtwoord inloggen: de plugin weigert de wachtwoordcontrole en logt de code
MFA_REQUIRED.Geen eigen
oidc.*- ofsaml.*-providers. Alleen de providers in deSocialProvider-enum.Geen API- of token-auth. De plugin is alleen voor panelsessies.
Geen Firebase multi-tenancy, inloggen via telefoon of anoniem inloggen.
Alleen Filament 5.
E-mails komen van Laravel. E-mails voor wachtwoordreset en verificatie gebruiken de notificaties van Filament, niet de e-mailtemplates van Firebase.
Registratie kan verraden waar een e-mailadres bestaat. De plugin toont dezelfde fout "email taken" voor een lokaal record en voor een account dat alleen in Firebase bestaat. De fout kan toch bij een andere stap van de validatie verschijnen. Een oplettende bezoeker kan de twee gevallen dus mogelijk van elkaar onderscheiden.
Geen import van lokale wachtwoordhashes in Firebase.
Beveiliging
Vind je een beveiligingsprobleem, gebruik dan Report a vulnerability op het Security-tabblad, of stuur een e-mail naar contact@isapp.be. Open geen publiek issue. Lees het beveiligingsbeleid.
Bijdragen
Lees CONTRIBUTING.md. Wijzigingen staan in CHANGELOG.md.
Credits
ISAP OÜ
Licentie
The MIT License (MIT). Lees LICENSE.md.