firevel/firebase-authentication
Laravel package for integrating Firebase Authentication: verify Firebase ID tokens, use a custom auth guard, and sync Firebase users with Eloquent models. Simplifies token-based login and user management for Laravel apps.
Install and wire in three steps
composer require firevel/firebase-authentication (it pulls in kreait/firebase-php, which does the actual JWT verification against Google's public keys).public/ and point the SDK at it — usually GOOGLE_APPLICATION_CREDENTIALS=/abs/path/service-account.json, or the FIREBASE_CREDENTIALS key if you publish config/firebase.php. The web user must be able to read the file.config/auth.php:'guards' => [
'firebase' => [
'driver' => 'firebase',
'provider' => 'users', // your existing Eloquent provider
],
],
Where to look first
config/auth.php — the guard entry is the whole integration surface.vendor/firevel/firebase-authentication/src — read the guard class first (it pulls the bearer token from the Authorization header, calls verifyIdToken, then hands the token's sub claim to the configured user provider). Everything you'll want to customize lives there or in your provider.vendor/kreait/firebase-php — token verification, leeway, revocation checks, and the key cache all belong to this SDK, not to the Laravel wrapper.First use case
A SPA or mobile app signs in with the Firebase client SDK, gets an ID token, and sends it on every request as Authorization: Bearer <idToken>. Your API routes are protected with auth:firebase; Laravel verifies the token and resolves a local User:
Route::middleware('auth:firebase')->get('/me', fn (Request $r) => $r->user());
Map Firebase UID to a local user (the pattern you'll actually use). Add a firebase_uid column with a unique index, then implement a provider that treats the token's sub as the identifier:
class FirebaseUserProvider implements UserProvider
{
public function retrieveById($identifier)
{
return User::where('firebase_uid', $identifier)->first();
}
public function retrieveByToken($identifier, $token) { /* not used */ }
public function updateRememberToken(Authenticatable $user, $token) {}
public function retrieveByCredentials(array $credentials) { return null; }
public function validateCredentials(Authenticatable $user, array $credentials) { return false; }
}
Register it in config/auth.php under providers.users (or a new firebase provider) and the guard now resolves Eloquent models with full relationships, policies, and $user->can(...) support.
Just-in-time provisioning. If you don't want a separate signup flow, create the row inside the provider's retrieveById or in a small middleware after authentication:
User::firstOrCreate(
['firebase_uid' => $payload->sub],
['email' => $payload->email, 'name' => $payload->name ?? null]
);
Wrap this in a DB transaction and handle the unique-constraint race when two requests from the same new user land simultaneously.
Use claim data, not just the UID. The decoded token carries email, email_verified, name, picture, phone_number, and any custom claims you set via the Admin SDK. Custom claims are the cheapest way to do role checks ($payload->admin === true) without a DB round trip — but treat them as a cache, not the source of truth, since they're only refreshed when the client refreshes its token.
Guard composition. auth:firebase on a route group is enough for most APIs. If you also support session-based admin logins, keep two guards and be explicit: Route::middleware('auth:firebase') for the API, auth:web for the dashboard. For $request->user() to be correct outside middleware, call Auth::shouldUse('firebase') in a service provider or middleware.
Testing. actingAs($user, 'firebase') works for feature tests and skips token verification entirely. For tests that need the real guard path, generate tokens with firebase/php-jwt using the Firebase emulator's signing key, or bind a fake guard. Don't hit Google's key endpoint in CI.
Client side. Let the Firebase SDK own the token lifecycle: call getIdToken() before each request (it refreshes automatically when within 5 minutes of expiry) and attach the result. Never cache ID tokens in localStorage for longer than the SDK does.
exp from the decoded payload before blaming the server.sub doesn't match any row yields Auth::user() === null — the request passes the JWT check and dies later on a null property. Log a warning in retrieveById when lookup misses.email claim is an account-takeover vector if a provider allows unverified emails. Gate it on email_verified === true and consider requiring a manual link step for existing password users.verifyIdToken validates signature and expiry; it does not confirm the user hasn't been disabled or signed out everywhere. For sensitive operations (password change, payment), pass the "check if revoked" flag through or call the Admin SDK's user lookup — otherwise a stolen token stays valid for its full hour.Authorization header disappears behind some proxies. Apache with mod_rewrite and certain nginx/FastCGI setups strip it. Check $_SERVER['HTTP_AUTHORIZATION'] before assuming the guard is broken.Authorization is not a simple header; your OPTIONS responses must allow it or every browser request fails before reaching the guard.auth('firebase')->user() is an App\Models\User.Auth::extend.kreait/firebase-php can change verification behavior underneath you. Pin the SDK version and cover the auth path with at least one integration test using a real token from the Firebase emulator.How can I help you explore Laravel packages today?