centivadev / filament-google-workspace-auth
Google Workspace auth for Filament users
Package info
github.com/centivadev/filament-google-workspace-auth
pkg:composer/centivadev/filament-google-workspace-auth
Fund package maintenance!
Requires
- php: ^8.2
- filament/filament: ^4.0 || ^5.0
- firebase/php-jwt: ^7.0
- spatie/laravel-package-tools: ^1.15.0
- spatie/laravel-permission: ^6.0 || ^7.0 || ^8.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^11.0
- pestphp/pest: ^5.0
- pestphp/pest-plugin-arch: ^5.0
- pestphp/pest-plugin-laravel: ^5.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-08-14 18:42:56 UTC
README
Google Workspace (OIDC) authentication for Filament v4/v5 using a dedicated FilamentUser model and Spatie roles/permissions.
Features
- 100% Google login (no username/password)
- Workspace domain restriction (
hd+ email domain) - Automatic user provisioning with avatar + last login timestamp
- Default role assignment on first login (configurable)
- Filament resources to manage users/roles/permissions (with protected roles)
- Policies + permissions-based authorization (Laravel Gate)
- Separate guard and model to avoid conflicts with a future
Usermodel - Session validity management: absolute timeout + near-real-time Google account revocation detection
- Ban and deactivation gates enforced before the account record is touched
- Rate-limited auth routes
Requirements
- PHP 8.2+
- Filament v4 or v5
- Laravel 11/12/13
spatie/laravel-permissionv6, v7 or v8
Tested against: PHP 8.4, Laravel 13, Filament v5 and
spatie/laravel-permissionv8. The lower bounds above are declared but not exercised in CI — the test toolchain (Pest 5) requires PHP 8.4 and Laravel 13. Filament v4 support in particular is unverified.
Installation
composer require centivadev/filament-google-workspace-auth
Publish config + migrations:
php artisan vendor:publish --tag="filament-google-workspace-auth-config" php artisan vendor:publish --tag="filament-google-workspace-auth-migrations"
Install Spatie permissions (migrations + config):
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="permission-migrations" php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="permission-config" php artisan migrate
Google Cloud Console Setup
- Create or select a Google Cloud Project
- Configure OAuth Consent Screen
- Type:
Internal(Workspace only) - Add your Workspace domain (
mydomain.com) - Add scopes:
openid,email,profile
- Type:
- Create OAuth Client ID
- Type:
Web application - Authorized redirect URI:
https://YOUR-FILAMENT-DOMAIN/auth/google/callback- Example:
https://admin.mydomain.com/auth/google/callback
- Type:
- Copy the Client ID and Client Secret into your
.env
FILAMENT_GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com FILAMENT_GOOGLE_CLIENT_SECRET=xxxx FILAMENT_GOOGLE_REDIRECT_URI=https://admin.mydomain.com/auth/google/callback FILAMENT_GOOGLE_HOSTED_DOMAIN=mydomain.com FILAMENT_GOOGLE_SUPER_ADMIN_EMAILS=admin@mydomain.com,cto@mydomain.com FILAMENT_GOOGLE_DEFAULT_ROLE=guest FILAMENT_GOOGLE_ROUTE_PREFIX=auth/google
Filament Panel Setup
Enable the plugin and remove password-based features from your panel provider:
use CentivaDev\FilamentGoogleWorkspaceAuth\FilamentGoogleWorkspaceAuthPlugin; return $panel ->login() ->plugins([ FilamentGoogleWorkspaceAuthPlugin::make(), ]);
Remove ->passwordReset() and ->emailVerification() from your panel provider to keep the login 100% Google.
FilamentUser model
HasRoles(Spatie) is required — it providesassignRole(), used for the default and super-admin roles.HasFilamentGoogleWorkspaceUseris recommended. It providesisBanned()andisActive(), which gate sign-in. A model without it still works and is still gated: the same rule is read straight offbanned_atandis_active. Override either method to customise the rule.
is_active is optional: models without that column are always considered active. It is owned by
your application, not by this package's migrations, so give it a database default.
use CentivaDev\FilamentGoogleWorkspaceAuth\Concerns\HasFilamentGoogleWorkspaceUser; use Spatie\Permission\Traits\HasRoles; class FilamentUser extends Authenticatable implements FilamentUserContract, HasAvatar, HasName { use HasFilamentGoogleWorkspaceUser; use HasRoles; protected $fillable = [ 'name', 'email', 'google_sub', 'avatar_url', 'last_login_at', 'email_verified_at', 'banned_at', 'is_active', ]; protected $casts = [ 'last_login_at' => 'datetime', 'email_verified_at' => 'datetime', 'banned_at' => 'datetime', 'is_active' => 'boolean', ]; }
Make sure the filament guard exists in config/auth.php and that filament-users provider uses the FilamentUser model.
Configuration
The published config file lives at:
config/filament-google-workspace-auth.php
Key options:
hosted_domainto restrict Workspace domainallowed_emailsto restrict to specific emailssuper_admin_emailsto auto-assignsuper-admindefault_roleto auto-assignguestguardto match your Filament guard (default:filament)routes.prefixto align with your Filament path (example:auth/googlefor a root‑domain panel)routes.throttleabuse ceiling on the redirect/callback routes, as"attempts,minutes"(default:120,1,nullto disable). Laravel keys this by IP and a whole Workspace office usually shares one NAT address, so keep it generous — the brute-force defence is PKCE/state/nonce, not this.
Access control
How accounts are matched
On callback, an account is looked up by google_sub or by email. The email match is what
lets you pre-create accounts (or migrate existing ones) and have them adopted by the first Google
login, which then stores google_sub as the permanent identity anchor.
This means anyone who can authenticate with an email already present in filament_users takes
over that row. That is safe because the email must be verified by Google and must pass your
domain/allowlist restrictions — but it does mean those restrictions are what protect the mapping.
Configure at least one of them.
Domain restriction
hosted_domain is only enforced when it is set. When set, both checks below must pass:
- the
hdclaim must match, when present — personal Gmail accounts carry nohd - the email must end with
@<hosted_domain>
The email suffix check is the one that stops personal Gmail accounts, so hosted_domain alone is
sufficient. Leaving it empty means any Google account can sign in unless allowed_emails is set.
Account status
For an existing account, sign-in is refused — before the record is written — when banned_at is
set or an is_active column is falsy. This resolves through isBanned() / isActive() when the
model uses HasFilamentGoogleWorkspaceUser, and off the attributes otherwise.
So banning or deactivating takes effect on the next login attempt, signing in never re-activates a
deactivated account, and a refused attempt leaves last_login_at and the profile fields untouched.
Newly provisioned accounts have no prior status to check; is_active takes your column default.
Admin UI
The plugin registers three resources (configurable):
- Filament Users
- Roles
- Permissions
They are grouped under the navigation group configured in resources.navigation_group.
Protected roles:
super-adminandguestcannot be deleted- The
nameof those roles is not editable
Base permissions:
- The package ships a migration stub
add_base_permissions.php.stubthat seeds default Filament permissions. - It also creates the
super-admin+guestroles if missing. - It assigns all permissions for the guard to
super-admin. Publish and run the package migrations to create them.
Authorization:
- Policies are registered for roles, permissions, and Filament users.
- Gate checks rely on Spatie permissions like
filament.users.*,filament.roles.*,filament.permissions.*.
Session Validity
The package provides two independent mechanisms to ensure sessions stay in sync with Google Workspace.
Remember me
Controls whether a persistent cookie is issued after login. When false (default), the session ends when the browser is closed.
FILAMENT_GOOGLE_REMEMBER=false
Absolute session lifetime
Forces the user to re-authenticate with Google after a fixed delay, regardless of activity.
FILAMENT_GOOGLE_SESSION_LIFETIME=480 # 8 hours, null to disable
This is independent from Laravel's native SESSION_LIFETIME (config/session.php). Both apply simultaneously — the one that triggers first wins:
| Setting | Type | Resets on activity? |
|---|---|---|
Laravel SESSION_LIFETIME |
Idle timeout | Yes — extends on every request |
FILAMENT_GOOGLE_SESSION_LIFETIME |
Absolute timeout | No — fixed since login |
Example: SESSION_LIFETIME=120 (2h idle) + FILAMENT_GOOGLE_SESSION_LIFETIME=480 (8h absolute).
A user active all day is kicked out after 8 hours. An idle user is kicked out after 2 hours.
Important: When
FILAMENT_GOOGLE_REMEMBER=true, the remember-me cookie bypasses Laravel'sSESSION_LIFETIMEentirely. In that case,FILAMENT_GOOGLE_SESSION_LIFETIMEis the only timeout enforced.
Google account revocation detection
Periodically calls the Google OpenID Connect UserInfo endpoint (https://openidconnect.googleapis.com/v1/userinfo) to verify the user's account is still active. If the account has been deleted or suspended in Google Workspace, the user is logged out immediately.
FILAMENT_GOOGLE_USERINFO_CHECK_INTERVAL=5 # every 5 minutes, null to disable
The check uses the access_token stored in the session (valid for 60 minutes after login). After that window, session_lifetime acts as the safety net.
Timeline:
Login
│
├─ 0–60 min ──── UserInfo check every N minutes ──── detection within N minutes
│
└─ 60 min+ ─────────── session_lifetime only ──────── detection at expiry
Network errors when calling the UserInfo endpoint are ignored (fail open) to avoid disrupting legitimate users during transient Google outages.
Notes
- This package does not use Socialite.
- All auth is OIDC with PKCE (S256), plus
stateandnonce. A callback whose session carries none of those values is refused — it never went through/auth/google. - The redirect and callback routes are rate limited (
routes.throttle). - If you want to disable auto-provisioning, set
FILAMENT_GOOGLE_AUTO_PROVISION=false.
Testing
composer test # Pest composer analyse # PHPStan, level 6 over src and tests composer format # Pint
Tests are fully offline: Google endpoints are mocked, no real credentials are required.
The dev toolchain (Pest 5, Testbench 11) requires PHP 8.4 and Laravel 13, which is narrower than what the package itself supports. See the note under Requirements.