Filament Firebase Auth

Connexion Firebase Auth pour les panneaux Filament 5 via les contrats d'authentification de Laravel — les mêmes utilisateurs et les mêmes mots de passe que vos autres applications Firebase.

Filament Firebase Auth by ISAPP

PackagistTestsPHPLicense

Présentation

Un plugin Filament 5 qui connecte les utilisateurs aux panneaux Filament avec Firebase Authentication. Il s'intègre via les contrats d'authentification de Laravel : un fournisseur d'utilisateurs firebase, un password broker firebase et un trait de modèle. Ainsi, les pages de connexion, de réinitialisation du mot de passe et de vérification de l'e-mail de Filament, les guards, les policies, canAccessPanel(), les limites de débit et les événements fonctionnent comme d'habitude.

À quoi il sert

Vos utilisateurs existent déjà dans un projet Firebase, par exemple pour une application mobile ou une autre application web. Ils doivent se connecter à vos panneaux Filament avec le même e-mail et le même mot de passe, ou avec le même compte Google.

Avec ce plugin, Firebase reste le seul endroit qui connaît les mots de passe. La base de données Laravel ne stocke ni mot de passe ni hash de mot de passe. Le plugin ajoute une table de liaison qui relie vos enregistrements locaux aux utilisateurs Firebase.

Fonctionnalités

  • Connexion par e-mail et mot de passe sur la page de connexion de Filament.

  • Connexion avec des fournisseurs sociaux (Google, Apple, Microsoft, GitHub, Facebook, X (Twitter), Yahoo) via le Firebase JS SDK.

  • Inscription : le plugin crée le compte Firebase, puis l'enregistrement local.

  • Réinitialisation du mot de passe sur les pages de Filament. Firebase reçoit le nouveau mot de passe.

  • Vérification de l'e-mail sur les pages de Filament. L'indicateur de vérification est partagé avec vos autres applications Firebase.

  • Page de profil : modification du nom, de l'e-mail et du mot de passe dans Firebase, "log out other sessions", "log out everywhere" en option et suppression du compte.

  • L'authentification multifacteur (MFA) de Filament pour la connexion par e-mail et mot de passe.

  • Un modèle, un guard et un ensemble de règles par panneau. Chaque panneau décide si une connexion crée un enregistrement local (panneau "ouvert" ou "fermé").

  • make:filament-user crée le compte Firebase et l'enregistrement local.

  • Une vérification de session en arrière-plan déconnecte une session quand le compte Firebase est supprimé ou désactivé, ou quand ses jetons sont révoqués.

  • La suppression du compte dans Firebase est optionnelle (opt-in), avec une valeur par défaut sûre (never).

  • Des points d'extension sous forme d'interfaces. Vous pouvez lier (bind) votre propre classe pour chacun d'eux.

Quand ne pas l'utiliser

  • Vous avez besoin d'une authentification par jeton pour une API (Sanctum, jetons d'ID de type bearer). Le plugin sert uniquement aux sessions de panneau.

  • Vous utilisez le multi-tenancy de Firebase (tenantId), la connexion par téléphone ou la connexion anonyme.

  • Vous utilisez Filament 3 ou Filament 4. Le plugin prend en charge Filament 5 uniquement.

Consultez la section Limites pour la liste complète.

Fonctionnement

  1. Le fournisseur d'utilisateurs firebase vérifie l'e-mail et le mot de passe auprès de Firebase. La page de connexion de Filament l'appelle via le contrat UserProvider de Laravel.

  2. Une fois que Firebase accepte les identifiants, le plugin trouve ou crée l'enregistrement local du modèle du panneau et le lie à l'UID Firebase dans la table firebase_identities.

  3. Les boutons sociaux connectent l'utilisateur via une popup Firebase dans le navigateur. Le serveur vérifie le jeton d'ID Firebase, puis effectue la même recherche.

  4. La session stocke une empreinte de l'état Firebase au lieu d'un hash de mot de passe. Le middleware AuthenticateSession de Laravel déconnecte la session quand cet état change.

  5. La réinitialisation du mot de passe, la vérification et les modifications du profil utilisent les pages de Filament et écrivent le résultat dans Firebase.

Consultez le guide d'architecture pour les détails.

Prérequis

  • PHP 8.3 ou ultérieur

  • Laravel 12.69.3 ou ultérieur, ou Laravel 13.33 ou ultérieur

  • Filament 5.8.3 ou ultérieur (Composer bloque les versions antérieures de Filament 5 en raison d'avis de sécurité)

  • Un projet Firebase avec un compte de service

  • kreait/laravel-firebase 7.2 ou ultérieur (Composer l'installe avec le plugin)

Gestion des versions

La version majeure du plugin correspond à la version majeure de Filament : le plugin 5.x fonctionne avec Filament 5.x. Au sein d'une même version majeure, les versions suivent le versionnage sémantique. Consultez RELEASING.md pour les règles et UPGRADING.md pour les mises à niveau entre versions majeures.

Installation

Installez le package :

composer require isapp/filament-firebase-auth

Publiez le fichier de configuration et la migration de la table de liaison :

php artisan filament-firebase-auth:install

Avant d'exécuter la migration, vérifiez le type de clé de vos modèles. La table de liaison utilise la colonne morphs() de Laravel. Elle suit Schema::morphUsingUuids() et Schema::morphUsingUlids(). Si les modèles de vos panneaux utilisent des clés UUID ou ULID, appelez l'une de ces méthodes dans un service provider, ou remplacez morphs() par uuidMorphs() ou ulidMorphs() dans la migration publiée.

php artisan migrate

Le JavaScript compilé des boutons sociaux est livré dans resources/dist. Vous n'avez pas besoin de npm. Publiez les assets Filament comme d'habitude, par exemple après chaque mise à jour :

php artisan filament:assets

Configuration de Firebase

Cette section vous mène de zéro à un projet Firebase que le plugin peut utiliser. La console Firebase change ses menus de temps en temps. Les consoles récentes placent Authentication sous Security. Si un libellé diffère, cherchez les mêmes mots à proximité.

1. Créer ou choisir un projet Firebase

  • Une autre application utilise déjà un projet Firebase (par exemple votre application mobile) : utilisez ce projet. Les utilisateurs partagés sont la raison d'être de ce plugin. Dans ce cas, gardez delete_accounts à never. Consultez la section Suppression de compte.

  • Pas encore de projet : dans la console Firebase, créez un nouveau projet, saisissez un nom de projet et confirmez. Google Analytics n'est pas nécessaire pour ce plugin.

Guide Firebase : Ajouter Firebase à votre projet JavaScript.

2. Activer les fournisseurs de connexion

Ouvrez Authentication → Sign-in method.

  • Email/Password : activez-le et enregistrez. La connexion par e-mail et mot de passe du plugin en a besoin. Guide Firebase.

  • Activez chaque fournisseur social que vous listez dans providers(). Chaque fournisseur autre que Google nécessite une application dans la console développeur de ce fournisseur. Pour la plupart des fournisseurs, la boîte de dialogue Firebase affiche une URI de redirection OAuth sous la forme https://YOUR-PROJECT-ID.firebaseapp.com/__/auth/handler. Déclarez cette URI (ou son domaine firebaseapp.com) dans l'application du fournisseur.

FournisseurCe que Firebase demandeGuide Firebase
GoogleActivez et enregistrez. La console peut demander une adresse e-mail d'assistance du projet. Aucune autre application n'est nécessaire.Google
AppleUne adhésion à l'Apple Developer Program : un Services ID, votre Team ID et une clé privée "Sign in with Apple" avec son key ID. Déclarez l'URI de redirection comme Return URL chez Apple.Apple
MicrosoftUne inscription d'application (app registration) dans Microsoft Entra ID (anciennement Azure Active Directory) : son ID client et un secret client. Déclarez-y l'URI de redirection. Utilisez providerParameters(SocialProvider::Microsoft, ['tenant' => '...']) pour n'autoriser qu'un seul tenant.Microsoft
GitHubUne OAuth app GitHub : son ID client et son secret client. Placez l'URI de redirection dans "Authorization callback URL".GitHub
FacebookUne application Facebook for Developers : son App ID et son App Secret. Placez l'URI de redirection dans les paramètres Facebook Login de l'application, dans la liste des URI de redirection OAuth.Facebook
X (Twitter)Une application développeur X : sa clé d'API et son secret d'API. Placez l'URI de redirection dans l'URL de callback de l'application.Twitter
YahooUne application développeur Yahoo avec les permissions OpenID Connect profile et email : son ID client et son secret client. Déclarez-y l'URI de redirection.Yahoo

3. Ajouter vos domaines aux domaines autorisés

Ouvrez Authentication → Settings → Authorized domains et cliquez sur Add domain. Ajoutez chaque domaine qui sert un panneau Firebase, par exemple admin.example.com. Pour le développement local, ajoutez aussi localhost, mais uniquement à un projet de développement : les nouveaux projets ne l'incluent plus par défaut, et Firebase le déconseille en production. Sans le domaine, la popup de connexion sociale échoue avec auth/unauthorized-domain.

Guide Firebase : la note "Authorized domains" dans Authentification par lien e-mail.

4. Créer les identifiants serveur (compte de service)

Le serveur communique avec Firebase via kreait/laravel-firebase avec un compte de service.

  1. Ouvrez Project settings → Service accounts.

  2. Cliquez sur Generate new private key, puis sur Generate key. Le navigateur télécharge un fichier JSON.

  3. Déplacez le fichier dans votre application, en dehors du répertoire public, par exemple storage/app/firebase/service-account.json.

  4. Ajoutez le fichier à .gitignore. Ne le committez jamais. Toute personne qui possède ce fichier peut gérer tous les utilisateurs du projet.

/storage/app/firebase/service-account.json
  1. Indiquez ce fichier à kreait/laravel-firebase dans .env. Un chemin relatif part de la racine de votre application :

FIREBASE_CREDENTIALS=storage/app/firebase/service-account.json

kreait/laravel-firebase lit aussi GOOGLE_APPLICATION_CREDENTIALS quand FIREBASE_CREDENTIALS est vide. En production, donnez à chaque serveur sa propre copie du fichier, par exemple via votre gestionnaire de secrets ou votre outil de déploiement.

Vous n'avez besoin du fichier de configuration de kreait/laravel-firebase que pour modifier autre chose que les identifiants (par exemple le timeout HTTP). Pour le publier :

php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" --tag=config

Laissez FIREBASE_AUTH_TENANT_ID vide. Le plugin ne prend pas en charge le multi-tenancy de Firebase.

Guide Firebase : Ajouter le SDK Firebase Admin à votre serveur.

5. Enregistrer une application web pour les boutons sociaux

Les boutons sociaux exécutent le Firebase JS SDK dans le navigateur. Ce SDK a besoin de la configuration d'une application web Firebase. Vous pouvez ignorer cette étape quand le panneau n'a aucun fournisseur social.

  1. Sur la page de présentation du projet, cliquez sur l'icône Web (</>). Quand le projet a déjà des applications, cliquez sur Add app, puis sur Web.

  2. Saisissez un surnom et cliquez sur Register app. Le plugin n'a pas besoin de Firebase Hosting.

  3. Copiez les valeurs de l'objet firebaseConfig dans .env :

Clé firebaseConfigClé env du plugin
apiKeyFIREBASE_WEB_API_KEY
authDomainFIREBASE_WEB_AUTH_DOMAIN
projectIdFIREBASE_WEB_PROJECT_ID
appIdFIREBASE_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

Vous retrouvez plus tard les mêmes valeurs sous Project settings → General → Your apps → Config.

La clé web apiKey n'est pas un secret. Elle identifie seulement votre projet Firebase, et chaque visiteur de la page de connexion peut la voir. Restreignez-la quand même : dans la console Google Cloud, ouvrez APIs & Services → Credentials et modifiez la clé. Sous Application restrictions, n'autorisez comme référents HTTP que les domaines de vos panneaux et votre authDomain (your-project-id.firebaseapp.com). La popup de connexion s'exécute sur l'authDomain et utilise aussi la clé. Sous API restrictions, n'autorisez que les API dont Firebase Authentication a besoin (Identity Toolkit API et Token Service API). Testez d'abord la modification sur une clé de test, car une mauvaise restriction casse la connexion.

Sur un projet Firebase partagé, les applications web du projet utilisent souvent la même clé de navigateur. Une restriction pour le seul panneau casse alors les autres applications : leurs domaines ne sont pas autorisés, et les API dont elles ont besoin sont bloquées. Donc, soit vous gardez dans la restriction les domaines et les API de toutes les applications qui utilisent la clé, soit vous créez une clé de navigateur distincte pour le panneau (APIs & Services → Credentials → Create credentials → API key), vous la placez dans FIREBASE_WEB_API_KEY et vous ne restreignez que cette clé. Consultez En savoir plus sur l'utilisation et la gestion des clés API pour Firebase.

6. Paramètres recommandés

  • Protection contre l'énumération des e-mails : ouvrez Authentication → Settings → User actions. Firebase l'active par défaut pour les projets créés à partir du 15 septembre 2023. Le plugin fonctionne qu'elle soit activée ou non, et affiche dans les deux cas le même message d'échec pour chaque connexion échouée.

  • Politique de mots de passe : ouvrez Authentication → Settings → Password policy pour exiger une longueur minimale ou des types de caractères. Les formulaires d'inscription et de profil de Filament vérifient Password::defaults() de Laravel. Définissez-y les mêmes règles, pour que les formulaires les vérifient aussi. Sur un projet partagé, la politique s'applique à toutes les applications. Si vous lui faites exiger la mise à jour des mots de passe non conformes à la connexion, les utilisateurs existants des autres applications peuvent être bloqués jusqu'à ce qu'ils changent leur mot de passe.

  • Modèles d'e-mails : le plugin n'utilise pas les modèles d'e-mails de Firebase. Les e-mails de réinitialisation du mot de passe, de vérification de l'e-mail et de changement d'e-mail viennent de Filament et de Laravel. Configurez l'envoi d'e-mails de Laravel (MAIL_MAILER et les autres paramètres) pour ces e-mails.

Guide Firebase : politique de mots de passe et protection contre l'énumération des e-mails.

7. Développement local avec le Firebase Auth Emulator

Vous pouvez développer sans vrai projet Firebase. Le Firebase Auth Emulator nécessite Node et Java 21 ou ultérieur.

npm install --save-dev --save-exact firebase-tools
npx --no-install firebase-tools emulators:start --only auth --project demo-your-app

Sans firebase.json, l'émulateur Auth écoute sur son port par défaut 9099. Pour fixer l'hôte et le port, ajoutez un firebase.json à votre application, comme le fait le plugin :

{
  "emulators": {
    "auth": { "host": "127.0.0.1", "port": 9099 }
  }
}

Définissez ensuite dans .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 envoie alors tous les appels Auth à l'émulateur. Un ID de projet qui commence par demo- ne nécessite aucun vrai projet Firebase. L'émulateur ignore les identifiants. La valeur fictive donne seulement l'ID de projet à kreait/firebase-php. Sans elle, kreait/firebase-php cherche des identifiants Google sur votre machine : une interrogation lente du serveur de métadonnées cloud, ou vos vrais identifiants. La connexion par e-mail et mot de passe, l'inscription, la réinitialisation du mot de passe, la vérification, la page de profil et make:filament-user fonctionnent avec l'émulateur. Les boutons sociaux ne fonctionnent pas avec l'émulateur : testez-les avec un vrai projet de développement. L'émulateur garde ses comptes en mémoire. Ils disparaissent après un redémarrage.

kreait/firebase-php lit FIREBASE_AUTH_EMULATOR_HOST depuis l'environnement du processus, pas depuis la configuration Laravel. Après php artisan config:cache, Laravel ne charge plus .env, donc la valeur dans .env n'a aucun effet. Définissez alors la variable dans l'environnement réel du processus (shell, Docker, gestionnaire de processus), ou ne mettez pas la configuration en cache en développement local.

Attention : ne définissez jamais FIREBASE_AUTH_EMULATOR_HOST en production.Avec cette variable, kreait/firebase-php ne vérifie pas la signature des jetons d'ID. N'importe qui pourrait alors envoyer un jeton falsifié à la route de connexion sociale et se connecter en tant que n'importe quel utilisateur.

8. Dépannage Firebase

SymptômeCause et solution
Le bouton social affiche "The browser blocked the sign-in window."Le navigateur a bloqué la popup (auth/popup-blocked). Autorisez les popups pour votre site et cliquez à nouveau.
Le bouton social affiche immédiatement "The sign-in failed. Try again."Souvent auth/unauthorized-domain : ajoutez le domaine aux domaines autorisés (étape 3). Ou auth/operation-not-allowed : activez le fournisseur dans Sign-in method (étape 2). Ou les valeurs FIREBASE_WEB_* sont incorrectes (étape 5).
"Unable to determine the Firebase Project ID"kreait/firebase-php n'a trouvé aucun identifiant. Vérifiez FIREBASE_CREDENTIALS et le chemin du fichier (étape 4). Avec l'émulateur, utilisez les identifiants fictifs (étape 7). Après une modification de .env, exécutez php artisan config:clear.
Une erreur pendant que kreait/firebase-php lit les identifiantsLe fichier n'est pas le JSON du compte de service, ou il est incomplet. Générez une nouvelle clé (étape 4). N'utilisez pas la configuration web ni un fichier google-services.json.
Chaque connexion par mot de passe échoue, et le journal indique que Firebase a échouéLe compte de service est incorrect ou supprimé, ou le serveur ne peut pas joindre Firebase. Le plugin journalise uniquement la classe de l'exception. Vérifiez l'étape 4 et le réseau.
Le bouton social affiche "The page has expired. Reload the page and try again."Le jeton CSRF de la page a expiré (HTTP 419), par exemple après une longue inactivité. Rechargez la page de connexion.

Configurer le guard, le fournisseur et le password broker

Chaque panneau Firebase utilise son propre guard. Le guard utilise un fournisseur d'utilisateurs avec le driver firebase. Le password broker du panneau utilise aussi le driver firebase.

// 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,
    ],
],

Le broker firebase stocke ses jetons dans une table de base de données, donc table est obligatoire. La table password_reset_tokens par défaut de Laravel fonctionne.

Configurer le panneau

La connexion, la réinitialisation du mot de passe et la vérification de l'e-mail utilisent les pages de Filament. Vous les activez comme d'habitude. L'inscription et le profil nécessitent les pages du plugin, car les pages de Filament stockeraient un mot de passe local. Pour l'authentification multifacteur (MFA), utilisez la classe AppAuthentication du plugin et son middleware de MFA obligatoire.

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']),
        );
}

Appelez ->id() avant ->plugin(). Le plugin lit le middleware du panneau au moment où vous l'ajoutez.

À chaque requête de panneau, le plugin vérifie cette configuration. Il vérifie chaque panneau dont le guard utilise le driver firebase, y compris un panneau sans le plugin. Le plugin lève une LogicException quand un tel panneau :

  • utilise la page d'inscription, la page de profil, l'authentification par application ou le middleware de MFA obligatoire de Filament,

  • a ->passwordReset(), mais son password broker n'utilise pas le driver firebase (sans ->authPasswordBroker(), Filament utilise le broker par défaut de config/auth.php),

  • a un modèle sans le trait HasFirebaseIdentity.

Le message indique la correction. Le plugin ne stocke jamais un mot de passe local en silence.

Gardez ->emailChangeVerification() sur un panneau avec la page de profil. Sans cette option, une modification de l'e-mail dans le profil change immédiatement l'e-mail Firebase partagé, sans aucune preuve que la personne possède la nouvelle adresse. Les réinitialisations du mot de passe de toutes les applications du projet vont alors à la nouvelle adresse.

Options du plugin

MéthodePar défautEffet
createOnSignIn(bool | Closure)trueAvec true, une connexion avec un compte Firebase qui n'a pas d'enregistrement local en crée un (panneau "ouvert"). Avec false, seuls les enregistrements déjà liés ou qui peuvent être liés se connectent (panneau "fermé").
providers(array)[]Les cas de SocialProvider qui reçoivent un bouton sur les pages de connexion et d'inscription, dans cet ordre. Aucun fournisseur : aucun bouton et aucune route de connexion.
providerScopes(SocialProvider, array)[]Scopes OAuth supplémentaires pour un fournisseur. Apple demande toujours email et name.
providerParameters(SocialProvider, array)[]Paramètres OAuth personnalisés pour un fournisseur, par exemple ['tenant' => '...'] pour Microsoft ou ['prompt' => 'select_account'] pour Google.

createOnSignIn() ne contrôle pas la page d'inscription. L'inscription dépend du réglage ->registration() de Filament.

Définissez les fournisseurs dans la configuration du panneau, pas plus tard à l'exécution. Le plugin ajoute la route de connexion uniquement à un panneau qui a des fournisseurs au moment où Laravel charge les routes (y compris avec php artisan route:cache).

Préparer votre table des utilisateurs

Le plugin ne stocke jamais de mot de passe. Votre table peut garder sa colonne password, mais la colonne doit accepter null. Sinon, la première connexion d'un nouvel utilisateur échoue avec un message qui renvoie à cette section.

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();
        });
    }
};

Vous pouvez aussi supprimer la colonne si aucun autre code ne l'utilise.

Le plugin attend aussi :

  • une colonne name et une colonne email dans la table,

  • name et email dans la liste $fillable du modèle (la création d'enregistrement par défaut les utilise).

Firebase stocke les e-mails en minuscules. Sur une base de données avec des comparaisons sensibles à la casse (SQLite, PostgreSQL), stockez aussi les e-mails locaux en minuscules. Sinon, un enregistrement local avec Jane@example.com ne correspond pas à l'utilisateur Firebase jane@example.com.

Préparer votre modèle

Ajoutez le trait HasFirebaseIdentity au modèle de chaque panneau Firebase. Le modèle implémente MustVerifyEmail et CanResetPassword, et utilise 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.
    }
}

Le trait lit l'indicateur de vérification dans la table de liaison et renvoie une empreinte de session depuis getAuthPassword(). markEmailAsVerified() et markEmailAsUnverified() écrivent l'indicateur dans Firebase, donc la modification apparaît dans toutes les applications du projet Firebase. markEmailAsVerified() lit d'abord l'utilisateur Firebase. Quand l'e-mail Firebase n'est pas l'e-mail local que le lien de vérification a prouvé, la méthode ne change rien et renvoie false. Le trait supprime aussi la liaison quand l'enregistrement est supprimé.

Si votre modèle utilise directement les traits Authenticatable ou MustVerifyEmail de Laravel (pas via Illuminate\Foundation\Auth\User), PHP signale une collision de méthodes. Indiquez à PHP d'utiliser les méthodes du trait du plugin :

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;
    }
}

Deux panneaux peuvent partager un modèle. Ils partagent alors un enregistrement par utilisateur Firebase. Deux panneaux avec des modèles différents obtiennent un enregistrement dans chaque modèle pour le même utilisateur Firebase.

Comment une connexion trouve l'enregistrement local

Pour chaque connexion (par mot de passe ou sociale), le plugin applique ces règles dans l'ordre :

  1. Une liaison existe pour cet UID Firebase et ce modèle : le plugin utilise son enregistrement.

  2. Aucune liaison, mais un enregistrement a le même e-mail : le plugin lie cet enregistrement, mais uniquement quand Firebase a vérifié l'e-mail. N'importe qui peut créer un compte Firebase non vérifié avec l'e-mail d'une autre personne. Un e-mail non vérifié ne lie donc jamais un enregistrement existant.

  3. Aucune liaison, aucun enregistrement, et le panneau est ouvert (createOnSignIn(true)) : le plugin crée l'enregistrement.

  4. Sinon, la connexion échoue.

La connexion échoue aussi pour un compte Firebase désactivé, pour un enregistrement lié à un autre UID Firebase et pour un utilisateur sans accès au panneau. Elle échoue aussi pour un utilisateur Firebase sans e-mail (par exemple un compte Twitter sans la permission e-mail), sauf si une liaison existe déjà. Chaque échec affiche le message de Filament "These credentials do not match our records". Ainsi, personne ne peut savoir quels e-mails existent.

La règle 2 fait confiance à l'indicateur emailVerified de Firebase, quel que soit le fournisseur qui l'a défini. Seuls les fournisseurs dont vous jugez l'e-mail fiable doivent pouvoir lier des enregistrements existants par e-mail.

Le driver firebase applique les identifiants supplémentaires et withQuery() de la même manière que le driver eloquent de Laravel. Par exemple, quand vous ajoutez 'is_active' => true aux identifiants de la page de connexion, un enregistrement avec is_active = false ne peut pas se connecter. Ces conditions limitent un enregistrement existant. Un nouvel enregistrement sur un panneau ouvert n'a pas à les respecter.

Connexion sociale

Ajoutez des fournisseurs avec l'enum SocialProvider : Google, Apple, Microsoft, GitHub, Facebook, Twitter et Yahoo. Les boutons apparaissent sous les formulaires de connexion et d'inscription de Filament via des render hooks. Le JavaScript se charge uniquement sur ces pages.

Le navigateur se connecte avec une popup Firebase et envoie le jeton d'ID Firebase à une route du panneau. Le serveur vérifie le jeton avec verifyIdToken(), y compris sa révocation. Le jeton doit :

  • provenir de l'un des fournisseurs de ce panneau (un jeton de mot de passe, anonyme ou personnalisé échoue),

  • prouver une connexion datant des 5 dernières minutes.

La route a le middleware du panneau, la vérification CSRF et une limite de débit de 5 tentatives par minute pour chaque panneau et chaque adresse IP.

La connexion sociale fait confiance au fournisseur et à son propre second facteur. Elle n'affiche pas le challenge multifacteur de Filament. Si vous avez besoin d'un second facteur pour ces utilisateurs, activez-le chez le fournisseur (par exemple dans Google Workspace ou Microsoft Entra). dutchcodingcompany/filament-socialite adopte la même approche. Consultez l'avertissement de la section Authentification à deux facteurs : cette règle s'applique aussi aux utilisateurs qui ont un mot de passe.

Authentification à deux facteurs

L'authentification multifacteur de Filament protège la connexion par e-mail et mot de passe. La page de connexion de Filament exécute le challenge comme d'habitude.

  • Utilisez Isapp\FilamentFirebaseAuth\MultiFactor\AppAuthentication au lieu de la classe AppAuthentication de Filament. Ses boîtes de dialogue de configuration, de désactivation et de codes de récupération vérifient le mot de passe auprès de Firebase. La classe de Filament compare un hash local, et ce hash n'existe pas.

  • Préparez le modèle pour l'authentification par application comme le décrit la documentation "Multi-factor authentication" de Filament : implémentez HasAppAuthentication (et HasAppAuthenticationRecovery pour les codes de récupération), utilisez les traits InteractsWith... correspondants, et ajoutez les colonnes app_authentication_secret (et app_authentication_recovery_codes).

  • L'authentification par code e-mail de Filament n'a pas d'étape de mot de passe. Elle fonctionne sans classe du plugin.

  • Quand l'authentification multifacteur est obligatoire, définissez le middleware du plugin avec ->multiFactorAuthenticationRequiredMiddlewareName(EnsureMultiFactorAuthenticationIsEnabled::class). Un utilisateur qui se connecte uniquement avec un fournisseur social passe alors. Tous les autres utilisateurs ont le comportement de Filament.

Avertissement : la connexion sociale contourne le challenge multifacteur de Filament pour chaque utilisateur, y compris pour un utilisateur qui a un mot de passe Firebase.Une personne qui connaît le mot de passe Firebase d'un compte peut se connecter directement à Firebase (par exemple via l'API REST de Firebase), lier son propre compte Google ou GitHub à cet utilisateur Firebase, puis utiliser le bouton social. Le plugin connecte alors cette personne sans challenge multifacteur de Filament. L'authentification multifacteur de Filament protège uniquement le formulaire e-mail et mot de passe. Pour réduire ce risque :

  • N'ajoutez pas de fournisseurs sociaux (->providers([...])) à un panneau qui exige l'authentification multifacteur. C'est la seule correction complète.

  • Gardez la valeur par défaut de Firebase : un compte par adresse e-mail (Authentication → Settings → User account linking). Elle empêche un second compte Firebase avec le même e-mail. Elle n'empêche pas l'étape de liaison décrite plus haut.

  • Laissez la protection contre l'énumération des e-mails activée, pour qu'un attaquant ne puisse pas trouver facilement les e-mails de vos comptes.

  • Exigez un second facteur chez le fournisseur pour les comptes importants (par exemple dans Google Workspace ou Microsoft Entra).

Les utilisateurs sans mot de passe Firebase ne voient aucune action d'authentification par application sur la page de profil. Le titre de la section peut quand même s'afficher, sans contenu.

Définissez le middleware du plugin chaque fois que le panneau utilise l'authentification multifacteur. La vérification de configuration lit isRequired une seule fois, au démarrage du panneau. Elle ne peut pas voir une closure qui dépend de l'utilisateur.

Pour un utilisateur qui se connecte uniquement via un fournisseur social, sur un panneau qui exige l'authentification multifacteur, le middleware interroge Firebase sur les fournisseurs de l'utilisateur une fois par requête.

Sessions

Vérification de session

Une autre application ou la console Firebase peut supprimer ou désactiver un compte, ou révoquer ses jetons (Firebase le fait lors d'un changement de mot de passe). Firebase ne notifie pas votre application. Le plugin vérifie donc l'utilisateur Firebase de chaque session en arrière-plan :

  • Après revalidate_every minutes (par défaut 15), la requête de panneau suivante lance une vérification.

  • Avec revalidate_queue à null, la vérification s'exécute après la réponse, dans le même processus. La requête n'attend pas Firebase.

  • Définissez un nom de file d'attente pour exécuter la vérification sur un worker de file d'attente. Le job utilise la connexion de file d'attente par défaut (QUEUE_CONNECTION). Sur la connexion sync, le job s'exécute immédiatement, et cette requête attend Firebase.

  • Quand le compte est absent ou désactivé, ou que ses jetons ont été révoqués, l'empreinte de session change.

  • L'empreinte change aussi quand l'e-mail devient vérifié, ou quand l'utilisateur Firebase perd le fournisseur password (par exemple quand le propriétaire de l'e-mail se connecte avec Google et que Firebase supprime un mot de passe non prouvé). Une personne qui a créé le compte avec l'e-mail de quelqu'un d'autre perd alors la session.

  • Le middleware AuthenticateSession de Laravel déconnecte alors la session à sa requête suivante, y compris une session "remember me".

  • Quand Firebase est injoignable, rien ne change. L'intervalle suivant réessaie.

Le plugin ajoute le middleware AuthenticateSession de Filament au panneau si le panneau n'en a pas. Définissez revalidate_every à 0 pour désactiver la vérification.

Déconnecter les autres sessions

La page de profil a une action "Log out other sessions". Elle déconnecte toutes les autres sessions de cet enregistrement dans cette application. La session courante reste connectée.

Se déconnecter partout

Définissez global_logout à true pour afficher une action "Log out everywhere" sur la page de profil. Elle révoque les jetons de rafraîchissement (refresh tokens) Firebase de l'utilisateur. Elle déconnecte donc aussi la personne de vos autres applications sur le même projet Firebase. Pour cette raison, elle est désactivée par défaut.

Un utilisateur qui se connecte uniquement avec un fournisseur social n'a pas de mot de passe Firebase. Pour cet utilisateur, "Delete account", "Log out other sessions" et "Log out everywhere" ne demandent aucun mot de passe, seulement une confirmation. Toute personne qui a volé une session d'un tel utilisateur peut donc utiliser ces actions. Avec delete_accounts = orphaned, un seul clic supprime alors le compte Firebase partagé.

Après un changement de mot de passe, un changement d'e-mail, "log out other sessions" et "log out everywhere", le plugin reconnecte la session courante avec la nouvelle empreinte. Laravel déclenche son événement Login pour cette reconnexion.

Suppression de compte

Le plugin supprime toujours la liaison locale quand un enregistrement est supprimé. La clé de configuration delete_accounts décide de ce qui arrive au compte Firebase :

delete_accountsCe que le plugin supprime dans Firebase
never (par défaut)Rien. Uniquement les données locales.
new_onlyUniquement un compte que Firebase a créé pendant cette connexion, quand un panneau fermé (createOnSignIn(false)) bloque la connexion. Par exemple, un inconnu se connecte avec Google sur un panneau fermé.
orphanedTout compte qui n'a plus aucune liaison dans cette application : après un blocage sur un panneau fermé, après que l'utilisateur a supprimé le compte sur la page de profil, ou après que du code ou un administrateur a supprimé l'enregistrement.

Avertissement : n'utilisez pas orphaned quand d'autres applications partagent le projet Firebase.Le plugin voit uniquement ses propres liaisons. Il ne peut pas savoir qu'une application mobile ou une autre application web utilise encore le compte. Avec orphaned, la suppression d'un enregistrement dans cette application supprime le compte Firebase pour toutes les applications, et la personne perd l'accès partout. Une connexion bloquée sur un panneau fermé provoque aussi une suppression : quand une personne sans liaison dans cette application se connecte avec le bon mot de passe ou avec un fournisseur social comme Google (par exemple un utilisateur de votre application mobile), orphaned supprime le compte Firebase de cette personne pour toutes les applications. Utilisez orphaned uniquement quand cette application est la seule à utiliser le projet Firebase. Gardez never dans tous les autres cas.

Autres points :

  • new_only compare la date de création et la date de dernière connexion du compte Firebase. Les deux doivent être à moins de new_account_window secondes (par défaut 60) l'une de l'autre et de l'instant présent.

  • La partie Firebase s'exécute après le commit de la transaction de base de données. Un échec Firebase n'empêche jamais la suppression locale. Le plugin journalise l'UID et la classe de l'exception.

  • Une suppression douce (soft delete) garde la liaison. Une suppression définitive (force delete) la retire.

  • Les suppressions en masse via le query builder (Admin::query()->where(...)->delete()) ignorent les événements de modèle. Elles ne retirent ni la liaison ni le compte Firebase. La liaison qui reste bloque cet UID Firebase dans ce modèle. Supprimez les enregistrements un par un, ou supprimez vous-même les lignes de liaison.

  • Quand l'inscription échoue après que le plugin a créé un compte Firebase, le plugin supprime ce compte dans tous les modes, car il l'a créé dans la même requête.

Créer des utilisateurs avec make:filament-user

Le plugin remplace la commande make:filament-user de Filament (et ses alias). Les options restent les mêmes :

php artisan make:filament-user --panel=admin --name="Jane Doe" --email=jane@example.com

La commande demande le mot de passe. Ne passez pas --password en ligne de commande : l'historique du shell le conserve.

Sur un panneau dont le guard utilise le driver firebase :

  • Aucun compte Firebase n'a cet e-mail : la commande en crée un avec un e-mail vérifié, puis crée l'enregistrement local et la liaison.

  • Un compte Firebase avec cet e-mail existe : la commande garde son mot de passe et le lie. Elle refuse un compte désactivé, un e-mail non vérifié et un compte qu'un autre enregistrement du modèle du panneau utilise déjà.

  • Quand l'étape locale échoue, la commande supprime le compte Firebase uniquement si elle a créé ce compte.

Sur tout autre panneau, la commande applique le comportement normal de Filament.

Points d'extension

Liez votre propre classe dans la méthode register() d'un service provider. Le plugin lie ses classes par défaut uniquement quand aucune autre classe n'est liée.

ContratPar défautQuand il s'exécute
Isapp\FilamentFirebaseAuth\Contracts\CreatesLocalUsersActions\LocalUsers\CreateLocalUserUne connexion, une inscription ou make:filament-user a besoin d'un nouvel enregistrement local.
Isapp\FilamentFirebaseAuth\Contracts\SyncsLocalUsersActions\LocalUsers\SyncLocalUserÀ chaque connexion et à chaque vérification de session. Copie le nom et l'e-mail Firebase.
Isapp\FilamentFirebaseAuth\Contracts\DecidesAccountDeletionDeletion\AccountDeletionPolicyLe plugin peut supprimer un compte Firebase (voir Suppression de compte).

Exemple : donner un rôle par défaut à chaque nouvel administrateur.

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() remplit uniquement les attributs de la liste $fillable du modèle. Ajoutez donc role à cette liste, ou utilisez 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);
}

La classe reçoit uniquement l'utilisateur Firebase. Les champs supplémentaires du formulaire d'inscription ne lui parviennent pas. Le résultat de mutateFormDataBeforeRegister() ne parvient pas non plus à l'enregistrement : la page du plugin construit l'enregistrement à partir de l'utilisateur Firebase. Pour sauvegarder des champs de formulaire supplémentaires ou des données modifiées, créez une sous-classe de la page Pages\Register du plugin et utilisez le hook afterRegister() de Filament.

Un SyncsLocalUsers personnalisé doit aussi mettre à jour les colonnes de liaison, sinon la vérification de session ne fonctionne plus. Le plus simple est d'étendre la classe par défaut :

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();
    }
}

Si vous écrivez la classe de zéro, injectez Isapp\FilamentFirebaseAuth\Actions\Identity\RefreshFirebaseIdentity et appelez sa méthode handle() avec la liaison de l'enregistrement.

Un DecidesAccountDeletion personnalisé s'exécute uniquement quand delete_accounts n'est pas never. En mode never, le plugin ne l'interroge jamais.

Tout le reste du comportement se trouve dans des classes d'action à usage unique dans Isapp\FilamentFirebaseAuth\Actions\..., chacune avec une seule méthode handle(). Vous pouvez les injecter dans votre propre code, par exemple Actions\Firebase\FindOrCreateFirebaseUser et Actions\Identity\LinkFirebaseIdentity pour créer des utilisateurs depuis une ressource Filament.

Configuration

config/filament-firebase-auth.php :

CléPar défautSignification
tablefirebase_identitiesLa table de liaison.
delete_accountsnevernever, new_only ou orphaned. Consultez la section Suppression de compte.
new_account_window60En secondes. Pour new_only, un compte plus récent que cette durée compte comme nouveau.
revalidate_every15Minutes entre deux vérifications de session. 0 désactive la vérification.
revalidate_queuenullFile d'attente du job de vérification de session, sur la connexion de file d'attente par défaut. null exécute la vérification après la réponse.
global_logoutfalseAffiche "Log out everywhere" sur la page de profil.
social_sign_injs_sdkMode de fonctionnement de la connexion sociale. js_sdk est la seule valeur.
web.api_key, web.auth_domain, web.project_id, web.app_idenvLa configuration web Firebase pour les boutons sociaux.

Les identifiants serveur restent dans la configuration de kreait/laravel-firebase (config/firebase.php).

Dépannage

"If you cannot sign in, reset your password."Un enregistrement local existant se lie à un compte Firebase uniquement quand Firebase a vérifié l'e-mail. Une réinitialisation du mot de passe prouve que la personne possède l'e-mail. Elle marque donc l'e-mail comme vérifié. La connexion suivante lie alors l'enregistrement.

La popup de connexion sociale est bloquée ou se ferme immédiatement.Consultez la section Dépannage Firebase.

"This email already has an account with another sign-in method."Par défaut, Firebase autorise un seul compte par adresse e-mail. La personne a créé le compte avec une autre méthode, par exemple un mot de passe. Connectez-vous avec cette méthode. Le plugin ne lie pas de fournisseurs à un compte Firebase existant.

La première connexion d'un nouvel utilisateur échoue, et le journal mentionne une colonne password NOT NULL.Consultez la section Préparer votre table des utilisateurs.

Une LogicException mentionne la page d'inscription, la page de profil, l'authentification par application ou le middleware de Filament.Utilisez la classe du plugin que le message indique. Consultez la section Configurer le panneau.

Un lien de réinitialisation du mot de passe échoue.La réinitialisation trouve l'enregistrement par e-mail. Quand une autre application a changé l'e-mail Firebase, l'e-mail local suit à la connexion suivante ou à la vérification de session suivante. D'ici là, la réinitialisation peut échouer. Les utilisateurs qui se connectent uniquement avec un fournisseur social ne reçoivent aucun e-mail de réinitialisation, car ils n'ont pas de mot de passe Firebase.

La connexion échoue, et le journal indique que Firebase a échoué.Le plugin affiche le message d'échec normal quand Firebase est injoignable. Il journalise uniquement la classe de l'exception, jamais le mot de passe ni le jeton. Vérifiez le compte de service et le réseau.

Tester votre application avec le Firebase Auth Emulator

kreait/firebase-php envoie ses appels Auth au Firebase Auth Emulator quand FIREBASE_AUTH_EMULATOR_HOST est défini. Tous les appels Firebase côté serveur du plugin passent par cette bibliothèque. Vos tests fonctionnels (feature tests) n'ont donc besoin d'aucun vrai projet Firebase.

L'émulateur nécessite Node et Java 21 ou ultérieur. Épinglez firebase-tools comme dépendance de développement de votre application, pour que chaque machine exécute la même version :

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 démarre l'émulateur, définit FIREBASE_AUTH_EMULATOR_HOST pour la commande et arrête l'émulateur ensuite. Un ID de projet qui commence par demo- ne nécessite aucun vrai projet Firebase.

L'émulateur ignore les identifiants, mais kreait/firebase-php a quand même besoin d'un ID de projet. Sans identifiants, la bibliothèque cherche les Application Default Credentials : une interrogation lente du serveur de métadonnées cloud, ou les vrais identifiants de votre machine. Donnez-lui des identifiants de compte de service fictifs dans l'environnement de test, comme le font les tests d'émulateur du plugin. La clé privée n'est jamais utilisée.

// 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',
]);

Les boutons sociaux dans le navigateur ne se connectent pas à l'émulateur.

Les tests unitaires et fonctionnels du plugin simulent (mock) Firebase. Un groupe de tests emulator s'exécute avec le Firebase Auth Emulator (composer test:emulator). Consultez CONTRIBUTING.md pour les exécuter.

Limites

  • Pas de passkeys. Firebase Authentication ne propose pas encore la connexion par passkey.

  • Pas d'authentification multifacteur Firebase. L'authentification multifacteur de Filament fonctionne. Un compte avec un second facteur Firebase (SMS ou TOTP, configuré dans une autre application) ne peut pas se connecter avec un mot de passe : le plugin refuse la vérification du mot de passe et journalise le code MFA_REQUIRED.

  • Pas de fournisseurs personnalisés oidc.* ou saml.*. Uniquement les fournisseurs de l'enum SocialProvider.

  • Pas d'authentification API ni par jeton. Le plugin sert uniquement aux sessions de panneau.

  • Pas de multi-tenancy Firebase, de connexion par téléphone ni de connexion anonyme.

  • Filament 5 uniquement.

  • Les e-mails viennent de Laravel. Les e-mails de réinitialisation du mot de passe et de vérification utilisent les notifications de Filament, pas les modèles d'e-mails de Firebase.

  • L'inscription peut révéler où un e-mail existe. Le plugin affiche la même erreur "email taken" pour un enregistrement local et pour un compte qui existe uniquement dans Firebase. L'erreur peut quand même apparaître à une autre étape de la validation. Un visiteur attentif peut donc distinguer les deux cas.

  • Pas d'import des hashs de mots de passe locaux dans Firebase.

Sécurité

Si vous trouvez un problème de sécurité, utilisez Report a vulnerability dans l'onglet Security, ou envoyez un e-mail à contact@isapp.be. N'ouvrez pas d'issue publique. Consultez la politique de sécurité.

Contribuer

Consultez CONTRIBUTING.md. Les modifications sont listées dans CHANGELOG.md.

Crédits

Licence

La licence MIT (MIT). Consultez LICENSE.md.