Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Spid Laravel Package

offline-agency/filament-spid

Filament plugin that adds SPID login to your admin panel, letting Italian users sign in with their digital identity. Configure the SPID providers, map SAML responses to users, and secure Filament access without building the integration yourself.

View on GitHub
Deep Wiki
Context7

Getting Started

Start with php artisan filament-spid:install. It is the fastest path from a fresh Laravel 12/13 + Filament 3–5 app to a working SPID login. Run it with --dry-run first to see the plan; it validates every answer before its first write, publishes config/migration/assets without overwriting, writes config/spid-auth.php, sets FILAMENT_SPID_* in .env, edits your panel provider (registering the plugin after ->login()) and bootstrap/app.php (CSRF exemption for the ACS) via PHP's tokenizer, and only migrates with --migrate. Anything it cannot do safely is printed as a manual step and the command exits 1 — treat that as "read the manual steps", not "it broke". Re-running with the same input is a no-op, so it is safe in provisioning scripts. examples/setup.public.json and examples/setup.private.json are the templates for --input=.

Before you run it, understand the two hard prerequisites: PHP 8.3+ and the patched italia/spid-laravel fork, with patching enabled and "minimum-stability": "beta" in your application's composer.json. Every published upstream italia/spid-laravel pins onelogin/php-saml 4.1.0 (CVE-2025-66475), which Composer refuses to install. Laravel 11 is also refused — it is EOL and carries unpatched advisories. If you are upgrading from 0.1.x, read the "Upgrading from 0.1.x to 1.0" section in the README before touching config, and check docs/upgrading.md.

The first useful day-to-day use case is a Filament panel login page with a SPID button. Once the button renders, your next stop is php artisan filament-spid:doctor — run it before you write any code. It is read-only (no writes, logins or network), checks dependency/advisory floors, the SPID patch (judged by the metadata php-saml actually builds, not by PATCHES.txt), panel and guard, user model, gate, fillable columns, SPID columns and unique index, SP identity and level, published assets, HTTPS and session cookie, the key pair (readable, matching, expiry, and not the example pair shipped with the library), and test IdPs in production. Fix everything it flags at level 0 before debugging a failed login.

Config surface you will actually touch:

  • filament-spid.panel (FILAMENT_SPID_PANEL) — pins the panel SPID users authenticate against.
  • filament-spid.minimum_level (default SpidL2) and spid-auth.sp_spid_level — both must agree; see Gotchas.
  • SPID_AUTO_CREATE_USERS — now false by default.
  • filament-spid.register_listeners — leave on unless you already own SPID event handling.
  • filament-spid.sp_profile / filament-spid.deployment (FILAMENT_SPID_SP_PROFILE, FILAMENT_SPID_DEPLOYMENT) — set early, they drive the metadata validator profile.

Implementation Patterns

Registration and the login flow. The shipped login view posts straight to italia/spid-laravel and authentication runs off its events; the plugin no longer exposes an ACS of its own and no longer overrides spid-auth.after_login_url. SpidPlugin::registerRoutes() now defaults to false — call ->registerRoutes(true) only if you want the helper routes back. In practice: register the plugin after ->login() in your panel provider, exempt spid-auth_acs from CSRF in bootstrap/app.php, and let the shipped HandleSpidLogin / HandleSpidLogout listeners provision the user, authenticate on the panel guard and tear the session down on logout. Applications need no listeners of their own; that is the intended default. Use SpidPlugin::resolve() to get the plugin for the current panel (or null) so shared views render safely outside a SPID panel.

Guard access before login. The listener calls FilamentUser::canAccessPanel() before logging the citizen in and refuses with a translated access_denied message. If you were previously relying on Filament's 403 to block users, you now get an explicit denial with no live SPID session hanging around — but you must still implement canAccessPanel() correctly on your user model. This is the single most common source of "login silently does nothing" reports.

Levels as a first-class concept. filament-spid.minimum_level and SpidLevel::rank() / meets() let you gate on SPID level programmatically. The library defaults to SpidL1, so set spid-auth.sp_spid_level to SpidL2 or higher — a URI, not an integer — and keep minimum_level at or below it. Use meets() in your own gates rather than string-comparing URIs.

Data minimisation. filament-spid.store_spid_data and filament-spid.spid_data_attributes limit what lands in spid_data; defaults preserve all eight attributes. If you store SPID payloads, tighten this to the attributes you actually use and document it — it is the cheapest GDPR/AgID-aligned decision you will make. SpidUserData::fromSpidAuth() accepts the SPIDUser object as well as an array, so you can normalise in one place.

Field mapping and creation-only mappers. The default field_mapping maps password to RandomPassword (the hash of a random password), so auto_create_users works against Laravel's stock users table where password is NOT NULL. Mappers implementing Mapping\Contracts\CreateOnly run on creation only, never on subsequent logins — implement that contract for anything you do not want re-applied (password rotation, one-time attributes). When update_user_data is on, changes to the mapping take effect on the user's next login, which matters during migrations.

Metadata validation in the pipeline. php artisan filament-spid:metadata:validate --url= (or --file=) with --profile=, --report=, --json and --strict wraps spid_sp_test (pinned 1.2.18). It fetches the exact metadata bytes over HTTPS with TLS verified, no redirects, size and time limits, and runs the tool with --extra and a JSON report. It reads both exit code and report: missing tool, wrong version, crash, timeout, missing xmlsec1, empty or unreadable report are exit 2, never a pass. Nothing is uploaded. Wire this into CI as a required job against your published metadata, and archive the report — it records the SHA-256 of the document, plugin and tool versions, profile and timestamp.

CI shape. Mirror upstream: every combination of PHP 8.3–8.5, Laravel 12–13 and Filament 3–5. php artisan filament-spid:doctor --json has a stable schema_version and exit codes 0/1/2; run it with --strict in CI so warnings fail the build, and keep it out of production runtime paths. docs/snippets/quickstart.sh is what CI runs on a fresh application — steal it for your own smoke test. There is also a local SPID lab under tools/spid with 111 real SAML Responses and sentinels that must fail, if you need to reproduce a misbehaving IdP.

Fluent button options now actually work. spidButtonLabel(), spidButtonIcon(), providers() (an allowlist over spid-idps) and showSpidButton(false) all take effect. showSpidButton(false) is the clean way to render a SPID panel's login page untouched during a staged rollout; providers() replaces the old config('spid-auth.providers') key entirely.

Boost resources. resources/boost/guidelines/core.blade.php and the spid-integration skill land when php artisan boost:install runs and the package is a direct dependency. Boost is not a runtime dependency — do not add it to production requirements.

Gotchas and Tips

  • The install fails before it starts. PHP 8.2 or Laravel 11 and Composer refuses outright. You need PHP 8.3+, Laravel 12/13, the patched fork, patching enabled and "minimum-stability": "beta". Pin the fork to a specific commit or tag; "patched" is an application-level guarantee, not a lockfile one.
  • sp_spid_level defaults to SpidL1, and that now means refused logins. SPID logins are refused while spid-auth.sp_spid_level is below filament-spid.minimum_level (default SpidL2), or while the minimum is not a SPID level URI. Set sp_spid_level to SpidL2 or higher and pass URIs everywhere. Boot-time configuration warnings are logged at most once an hour per message through the cache — so a warning in laravel.log is not "once per request noise", it is the one signal you get. Grep for it.
  • No more accounts for free. auto_create_users is false by default; a SPID identity no longer creates an account unless you opt in via SPID_AUTO_CREATE_USERS. Flip it deliberately, and pair it with a sane canAccessPanel().
  • @spid.invalid, not @spid.local. Without an email from SPID, the default mapping stores <fiscal code, lowercased>@spid.invalid because .local resolves on a LAN. Existing accounts change on their next login when update_user_data is on — if anything keys off the email column (analytics, mail suppression lists, lookups by domain), reconcile before rolling out.
  • Renamed and removed config keys. redirect_after_login, spid_level and providers are gone, along with SPID_REDIRECT_AFTER_LOGIN and SPID_LEVEL. Use spid-auth.after_login_url, spid-auth.sp_spid_level and SpidPlugin::providers(). A stale key silently does nothing — doctor is how you catch that.
  • Panel misconfiguration is now an exception. A filament-spid.panel naming no registered panel throws SpidPanelNotFoundException instead of quietly logging the citizen in on the default guard. That is a security improvement and a deployment hazard: if your panel ID differs between environments, catch it in CI with doctor, not in production.
  • No ACS of your own. The plugin's ACS endpoint and SpidPlugin::acsRoute() are gone; italia/spid-laravel owns spid-auth_acs. Anything that referenced acsRoute() — route caching tricks, CSP allowlists, reverse-proxy rules, WAF exceptions — needs updating. Remember the CSRF exemption is on the library's route name.
  • registerRoutes() is false by default. If you upgraded and helper routes vanished, this is why. Call ->registerRoutes(true), but only if you actually need them.
  • Installer exit 1 is not a crash. It means some steps were listed as manual. Read the output. Also: it will not overwrite existing config/migration/asset files, and it only rewrites config/spid-auth.php while that file is still the library's copy or what the installer itself wrote — otherwise it prints the values for you to paste. Do not expect it to reconcile a hand-edited config.
  • doctor reports CSRF and CSP as skip by design. The CLI cannot observe them, so a green doctor run does not prove login works. Verify the ACS exemption and your CSP by hand at least once, and rely on the quickstart smoke test for the rest.
  • metadata:validate exit 2 is an error, not a pass. Missing xmlsec1, a tool-version mismatch or an empty report all land there. And no matter how green the report: it states HOSTED_VALIDATOR_NOT_RUN and FORMAL_AGID_ACCEPTANCE_NOT_OBTAINED. Passing the local validator is not AgID acceptance — budget for the hosted validator separately.
  • Logout has three outcomes. SpidController::logout() hands a SPID session to the IdP single logout and lets HandleSpidLogout end it; other sessions are logged out of the panel guard; an unreachable IdP still ends the local session. Test all three, especially the unreachable-IdP path — it is the one that only shows up during an IdP outage.
  • ->login() after the plugin clobbers the SPID login page. It now logs a warning when that happens, but the warning is all you get. Order matters in the panel provider.
  • jQuery moved. 3.7.1 ships with the package as an on-request Filament asset rather than coming from wherever you previously got it. If you had a jQuery version pinned elsewhere, check for a double-load.
  • Extension points worth knowing: Mapping\Contracts\CreateOnly for mappers that must not re-run; SpidLevel::meets() for gates; SpidPlugin::resolve() for views that may render outside a SPID panel; filament-spid.register_listeners if you insist on owning the listeners; filament-spid.store_spid_data / spid_data_attributes for data minimisation. Reach for these before forking the package — SemVer now applies from 1.0.0, so the public API is something you can actually build on.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager