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

Symfony Bridge Laravel Package

lightsaml/symfony-bridge

Symfony bridge for LightSAML that integrates SAML 2.0 service provider authentication with Symfony Security. Provides configuration, firewall integration, and user loading so Symfony apps can add SSO via existing SAML identity providers.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require lightsaml/symfony-bridge
// app/AppKernel.php (Symfony 2/3) or config/bundles.php (Symfony 4)
$bundles[] = new \Lightsaml\SymfonyBridgeBundle\LightsamlSymfonyBridgeBundle();

Look here first, in this order

  1. vendor/lightsaml/symfony-bridge/DependencyInjection/Configuration.php — the bundle's docs were thin even when it was maintained; this file is the authoritative list of every config key.
  2. vendor/lightsaml/symfony-bridge/Resources/config/*.xml — the service IDs you will inject or override.
  3. vendor/lightsaml/symfony-bridge/Controller/ — the built-in metadata/login/logout endpoints and the routes they expect.
  4. vendor/lightsaml/lightsaml/src/LightSaml/ — the actual SAML implementation; the bridge is a thin DI wrapper, so most real work happens here.

Minimal config (config.yml), the shape you will end up with as an SP:

lightsaml_symfony_bridge:
    sp:
        entity_id: "https://app.example.com/saml/metadata"
        credentials:
            - certificate: "%kernel.project_dir%/var/saml/sp.crt"
              private_key: "%kernel.project_dir%/var/saml/sp.key"
    idp:
        entity_id: "https://idp.example.com/metadata"
        single_sign_on_service:
            url: "https://idp.example.com/sso"
        single_logout_service:
            url: "https://idp.example.com/slo"
        x509_certificate: "%kernel.project_dir%/var/saml/idp.crt"

First use case: SP-initiated SSO. Enable the bundle's routes (or copy them), point your browser at the login route, get redirected to the IdP, land back on the assertion-consumer endpoint, and read the resulting LightSaml\Model\Protocol\Response out of the container. Publish /saml/metadata immediately — you will need it to register your app with the IdP before anything else works. Do metadata exchange before writing a single line of application code.

Implementation Patterns

1. Let the bundle own the protocol, let your app own the user

The bridge's opinionated piece is that authentication is delegated to a "user factory" service that receives the SAML assertion/response and returns your UserInterface. Implement it and register the service:

class SamlUserFactory implements LightsamlUserFactoryInterface
{
    public function __construct(private UserRepository $users) {}

    public function createUser(Assertion $assertion): UserInterface
    {
        $nameId = $assertion->getSubject()->getNameID()->getValue();
        $email  = $this->attribute($assertion, 'urn:oid:0.9.2342.19200300.100.1.3');

        $user = $this->users->findOneBy(['email' => $email])
            ?? throw new AuthenticationException("Unknown SAML subject: {$nameId}");

        // Do NOT trust IdP role attributes blindly; map them explicitly.
        return $user;
    }
}

Then wire it into the firewall's lightsaml factory (exact key names vary by version — check Configuration.php). The listener intercepts the configured check path, validates the response through the LightSAML container, calls your factory, and hands the result to Symfony's token storage.

2. Reach through to the container for anything non-standard

The bridge exposes the underlying LightSaml\Container\ContainerInterface and its collaborators as services (lightsaml.container, binding factories, request/entity stores). For a custom assertion-consumer controller:

public function acs(Request $request, ContainerInterface $c): Response
{
    $response = (new HttpPostBinding())->receive(
        $c->getBindingFactory(),
        $request
    );

    $c->getSignatureValidator()->validate($response);
    $c->getStatusResponseValidator()->validate($response);
    $c->getAssertionValidator()->validate($response->getAssertion());

    // hand off to your own login flow
}

This is the pattern to reach for when the bundle's built-in flow doesn't match your routing (e.g. per-tenant ACS endpoints, /saml/{tenant}/acs).

3. Metadata as a first-class artifact

Generate SP metadata from the same container so entity ID, endpoints, and certificate never drift out of sync with runtime config. Cache the IdP metadata as a local XML file checked into the repo or deployed with the app — do not fetch the IdP metadata over HTTP at request time. The bundle's metadata store will happily hit the network on a cache miss, and a slow IdP becomes a slow login for every user.

4. Multi-IdP / discovery

For more than one IdP, do not try to make the single idp: config block do it. Register multiple entity descriptors with the entity store and select by a route parameter or a pre-login form, building the AuthnRequest per request. Keep a thin service that maps "tenant → IdP entity ID → descriptor" and let the container do the rest.

5. Local test loop

Spin up SimpleSAMLphp or a Keycloak container as the IdP, register your /saml/metadata, and keep the browser SAML-tracer extension open while you iterate. Do this before touching production IdP settings — debugging signature failures against a real corporate IdP is a bad time.

Gotchas and Tips

The package is abandoned; plan accordingly. Last release was May 2018. It targets Symfony 2.8–4.0-era APIs (the old security firewall factory pattern, AppKernel). Composer will refuse to install it alongside Symfony 5/6 — and on Symfony 4.4 you are relying on the compatibility shims. If you are on a modern Symfony, budget for either vendoring the handful of classes you need or dropping the bridge entirely and wiring lightsaml/lightsaml (or the litesaml fork) into your own services. Treat this bundle as a reference implementation, not a maintained dependency.

PHP 8 / xmlseclibs friction. The underlying robrichards/xmlseclibs release pulled in by this generation of LightSAML has known issues on PHP 8 (openssl_* behaviour, deprecated signatures). Pin a compatible xmlseclibs or be prepared to patch. Test signature validation first on your target PHP version before building anything on top.

Certificate formatting is the number one support ticket. The config accepts both a file path and an inline PEM string, and the two are easy to mix up. IdP certs must be the bare base64 body or a full PEM block consistently — a cert with the -----BEGIN CERTIFICATE----- wrapper where the library expects the raw body (or vice versa) fails with an opaque "signature invalid" error. Normalize certificates through a single helper.

Clock skew. SAML validates NotBefore / NotOnOrAfter against the local clock. Container clocks drift; a 30-second skew causes intermittent, maddening login failures that "fix themselves". Ensure NTP is running on every web node and prefer validating against a small leeway if the library version supports it.

Session-backed request state is single-flight. The request-ID store lives in the session. Two tabs starting login simultaneously will clobber each other, and the second response fails with "InResponseTo mismatch". Either accept it and show a friendly error, or key the store by a per-attempt nonce.

Stateful firewall required. The listener needs the session. A stateless: true firewall will appear to authenticate and then silently lose the token. Also make sure your /saml/* routes are under a firewall with anonymous: ~ so the ACS request can reach the controller before authentication.

Route collisions. The bundle registers routes like /saml/login, /saml/metadata, /saml/logout. If you already have a route or a firewall login_path at one of those paths, the later definition wins and you get confusing redirects. Check debug:router after installing.

Signed response vs signed assertion. IdPs differ. Some sign the Response, some sign the Assertion, some both. If you validate only one and the IdP does the other, authentication fails. Read the IdP's metadata (<KeyDescriptor use="signing"> on the SP/IdP roles) and validate the thing that is actually signed.

Logging is your debugger. Turn on the bundle's debug/logging flag and dump raw SAMLRequest/SAMLResponse XML to a log channel during development. The error messages from LightSAML are terse; the raw XML plus the IdP's own logs is how you actually diagnose failures.

SLO is a trap. Single Logout is implemented inconsistently across IdPs and rarely worth the integration cost. Most production deployments do local Symfony logout plus a best-effort redirect to the IdP's SLO endpoint, and accept that the IdP session may survive.

Extension points. The bundle's service definitions are plain XML — override any service ID in services.yml (or with a compiler pass) to swap in your own binding, validator, or store. That is the cleanest way to add per-tenant behaviour without forking. Decorate rather than replace where possible, so future (hypothetical) bundle updates don't break you.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource