javer/sp-bundle
javer/sp-bundle is a PHP package that organizes reusable service-provider logic into a compact bundle, helping developers register and configure shared services with less boilerplate. It offers a simple integration point for projects that need lightweight, maintainable service setup.
First, calibrate the fit. javer/sp-bundle is a Symfony bundle, not a Laravel package. It exists to register the lightsaml core library inside a Symfony kernel: an autoloaded bundle class, a lightsaml_sp config tree, DI-wired services, and a Symfony security firewall listener. None of that machinery exists in Laravel. So a Laravel dev has three realistic entry points, in order of cost:
lightsaml/lightsaml library directly inside a Laravel service provider, and write your own routes/controllers for the SP endpoints. You get the protocol logic without the Symfony yoke.Check the version matrix before anything else. As of 2.1.0 the bundle requires PHP 8.1+ and adds Symfony 7 support. That means:
composer.json for the exact constraint ranges — the fork exists to keep the Symfony ceiling raised, so the supported range is precisely what this release changed. If you're pinned to an older Symfony that 2.1.0 dropped, pin the bundle to 2.0.x deliberately.Minimal Symfony-side steps (if you take option 2):
composer require javer/sp-bundle (2.1.0 for Symfony 7 / PHP 8.1+; check composer.json for the Symfony and lightsaml versions it pins).Javer\SpBundle\SpBundle (or the namespace it ships with) in config/bundles.php.config/saml/idp.xml.openssl req -new -x509 -days 3650 -nodes -out sp.crt -keyout sp.key.lightsaml_sp tree: sp.entity_id, sp.credentials (cert + key), idp metadata file, and trust settings for the assertions you will accept.lightsaml: ~ under firewalls.main, with your user provider) and confirm the bundle's routes are loaded: login, acs, metadata, logout, discovery.First use case to validate: SP-initiated login against a single IdP — GET /saml/login → redirect to IdP → POST back to /saml/acs → authenticated session. If that loop works end to end, you have a foundation; everything else (SLO, metadata rotation, multi-IdP) is iterative.
Where to look first: the bundle ships very few classes — the interesting behavior lives in lightsaml/lightsaml (LightSaml\Sp\...). Learn the core library, not the bundle.
SP-initiated vs IdP-initiated. Most corporate IdPs (Entra ID, Okta) support both; the bundle handles the IdP-initiated POST directly to /saml/acs if your firewall is configured for it. Prefer SP-initiated for normal app logins (better UX, cleaner error handling), reserve IdP-initiated for the "app tile in the IdP portal" flow.
Metadata as the contract. Treat metadata.xml (what you publish) and idp.xml (what you consume) as first-class config artifacts, checked into a private repo or a secrets manager, with a documented rotation cadence. The bundle's /saml/metadata endpoint is what you send to the IdP admin.
Attribute mapping. The default user provider expects a stable identifier (usually NameID, sometimes email or an objectGUID). Override with a custom SamlUserProvider implementation, or the bundle's user-creator interface if it exposes one — look at LightSaml\SpBundle\Security\User\* and mirror the pattern. This is also where you map groups/roles from the assertion into your app's authorization model. Log the raw attributes once in staging — the exact claim names (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress etc.) vary wildly between IdPs and are unguessable.
Session handoff to Laravel. In the sidecar pattern, the ACS controller mints a short-lived signed JWT (or sets a cookie on a shared parent domain / *.example.com) after successful assertion processing, redirects to the Laravel app, and Laravel validates the token and establishes its own session. Keep the TTL short (~60s), sign with an asymmetric key, and never pass IdP attributes through unfiltered.
Multi-IdP. The bundle supports multiple entity descriptors / credentials; if you only ever have one IdP, resist the temptation to build for N. The complexity of discovery (which IdP for which user) is real — start with one and add a "Discovery" endpoint when a second IdP actually appears.
Logout. SLO is where these integrations most often break. The bundle wires a logout listener; verify it against your specific IdP's expectations (does it want a LogoutRequest, a LogoutResponse, both, or just a redirect?). Test both SP-initiated and IdP-initiated logout — most IdPs do one but not the other well.
Integration tips. Put the SAML sidecar behind the same hostname if possible (e.g. /saml/* reverse-proxied) rather than a separate subdomain — cookie and SameSite behavior is far simpler. If you must subdomain, set the session cookie domain explicitly and test the redirect flow in Safari (ITP) and Chrome (3rd-party-cookie restrictions) before shipping.
Upgrading an existing sidecar to 2.1.0. The release is small but it is not a no-op: PHP 8.1 becomes a hard floor, and Symfony 7 support means the dependency graph moved. Bump PHP in the sidecar image first, run composer why-not / composer update --dry-run to see what else is pinned to the old Symfony line, and only then raise the bundle constraint. Treat it as a dependency-graph change, not a code change, and re-run your full login + logout smoke test afterwards — nothing in the notes suggests bundle code changed, but the transitive lightsaml/Symfony resolution can shift.
The maintenance risk is still the headline, but softened slightly. 1 star, near-zero dependents, a single-person fork of lightsaml (which has been largely dormant for years). No community, no issue triage, no security response channel. However, 2.1.0 is evidence the fork is doing the one job it was created for: tracking upstream Symfony/PHP releases (Symfony 7, PHP 8.1, refreshed CI). That is a meaningful signal versus a truly abandoned fork. It is still fine for a small internal integration with a stable IdP; still a bad bet for anything you'd call "production identity." Budget for eventual replacement (switch to a Laravel-native SAML package, or move auth to OIDC, which every major IdP now supports and which has far better tooling).
The CI-only changes are not a feature. "Update GitHub Actions" is maintenance hygiene — don't read it as a stability guarantee, and don't assume it means anything was tested for you. Verify the release works in your sidecar before rolling it out; you are the test suite.
Signature and assertion validation. The most common silent failure is an accepted assertion that shouldn't have been accepted. Explicitly configure which signatures you require (response-level and/or assertion-level), the signing algorithm (SHA-256 minimum — reject SHA-1), allowed clock skew, and the audience / destination / recipient checks. Default configurations in older LightSAML versions have historically been permissive; audit them. A Symfony 7 / PHP 8.1 bump does not change these defaults — re-audit them on upgrade anyway, since this is the one area where a dependency bump can quietly alter behavior.
Cert and metadata rotation. IdP certs expire on a schedule the IdP admin knows and you don't. When it rotates, every login breaks at once with an unhelpful XML-signature error. Establish a rotation runbook: how do you get the new cert, where does it live, how is it deployed, how do you verify before the old one is retired. Same for your SP cert.
Clock skew. SAML assertions carry NotBefore / NotOnOrAfter. Container clocks drift; NTP in Docker is not automatic. A 30–60 second skew allowance on both sides is standard; more than that is a smell.
NameID format. Some IdPs default to transient (a fresh opaque identifier per login). If your app assumes a stable user ID, that silently breaks. Set the format explicitly in the authn request (persistent or emailAddress are the usual safe choices) and confirm the IdP honors it.
Debugging. You will need the raw SAML request/response XML. Enable the bundle's logger (or hook into the Symfony logger service), and keep the XML in local dev — but never log assertions in production, they contain PII and are authentication tokens. A useful trick: put a dump-style listener on the ACS that writes the decoded XML to a file in dev only; you'll use it constantly.
Extension points. The bundle is thin, so extension is mostly at the LightSAML level: a custom BindingResolver (if you need to force Redirect vs POST), a custom attribute mapper, and your own user provider. Anything beyond that probably belongs in your Laravel app, not in the sidecar.
If you go the "use lightsaml/lightsaml directly in Laravel" route, expect the missing pieces: no config tree, no firewall, no routes, no user-provider scaffold. You will write ~10 files worth of glue (SP controller, ACS controller, metadata controller, credential/entity store wiring, session persistence). It's tractable, and it sidesteps the Symfony coupling entirely — but only do it if the protocol logic is what you actually need, not the whole bundle. Note you'd also be bypassing this fork's Symfony-7 maintenance work, so check which lightsaml version it actually pins and whether that core library supports your PHP runtime.
One last sanity check before committing: does the IdP you're integrating with support OIDC? If yes, and your app has no requirement for the SAML-specific features (multi-tenant federation, specific attribute contracts, existing SP contract with the IdP), OIDC via a mature Laravel package is almost always the less painful path — and it keeps you out of the LightSAML maintenance footprint entirely.
How can I help you explore Laravel packages today?