lightsaml2/lightsaml-idp
SAML 2.0 Identity Provider for Laravel built on LightSAML. Configure service providers, signing certificates, and assertion attributes, then expose metadata, SSO, and logout endpoints for enterprise single sign-on integrations.
This package is a thin IdP-side layer on top of lightsaml/lightsaml (the core SAML library). It does not give you a turnkey IdP application — you still own routing, user authentication, SP registration, and persistence. Treat it as the SAML message/XML/profile plumbing around which you build your own controller.
Typical install and orientation:
composer require lightsaml2/lightsaml-idp
# if not reachable via Packagist, add a VCS repository:
# "repositories": [{"type":"vcs","url":"https://github.com/lightsaml2/lightSAML-IDP"}]
Look at these first, in this order:
README.md and any example/ / demo/ directory — the canonical usage is a demo IdP endpoint.src/Builder/ — the profile builders are the actual API you will call (SsoIdpResponseBuilder-style classes). This is the heart of the package.src/Store/ — request/response/session stores (the in-memory implementations are for examples; you will replace them).tests/ — the most reliable documentation of expected inputs and outputs.Your first end-to-end use case is the SP-initiated Web Browser SSO flow:
SP browser → redirect with SAMLRequest
→ your /sso controller receives it
→ parse + validate AuthnRequest (issuer = known SP, ACS URL allowed, signature if required)
→ your app authenticates the user (forms, session, SSO to an upstream)
→ build a signed SAML Response with NameID + attributes
→ POST (HTTP-POST binding) to the SP's AssertionConsumerService
You will need, at minimum:
User representation the package can read attributes and NameID from.Wrap the builders in your own service class. Do not let the package's demo controller become your architecture. A thin SamlIdpService that takes an AuthnRequest string and returns a Response string (or throws a domain exception) keeps SAML quirks out of your controllers and makes it testable.
Controller shape (Laravel, but the pattern generalises):
Route::get('/saml/sso', [SamlController::class, 'sso']); // HTTP-Redirect binding
Route::get('/saml/metadata', [SamlController::class, 'metadata']);
public function sso(Request $request) {
$authnRequest = $request->query('SAMLRequest');
$relayState = $request->query('RelayState');
// 1. parse & validate (issuer, ACS URL, signature, destination)
// 2. resolve authenticated user via your own guard
// 3. build response
$response = $this->idp->buildResponse($authnRequest, $user);
return view('saml.post', ['acs' => $acsUrl, 'response' => $response, 'relayState' => $relayState]);
}
SP registry as data, not code. Keep SPs in a database/config table keyed by entity ID. Validate the Issuer in the AuthnRequest against this registry, and validate that the requested ACS URL is in that SP's allowed list — never trust the AssertionConsumerServiceURL in the request without checking it against registration.
Session/state store. SAML is stateful across the redirect: you need the original request ID to populate InResponseTo, and you need to detect replays. Persist {request_id, sp_entity_id, relay_state, expires_at} in your cache/session, and check it on the response path.
Attribute mapping layer. Keep an explicit map from your user model to SAML attributes (urn:oid:2.5.4.42 givenName, urn:oid:2.5.4.4 sn, mail, eduPersonPrincipalName, etc.). Make it configurable per SP, since different SPs expect different NameID formats (persistent, transient, emailAddress, unspecified).
Metadata. Generate IdP metadata dynamically from your config (signing cert, SSO endpoint, supported bindings). On the SP side, import SP metadata where possible rather than hand-entering entity ID + ACS URL. Metadata is also your best debugging artifact — compare what you publish with what the SP expects.
Signing strategy. Decide early: sign the Assertion, or sign the Response, or both. Many SPs only accept signed assertions; some (notably ADFS-style) prefer signed responses. Make this per-SP configuration, not a global flag.
Testing. Write integration tests that feed real captured AuthnRequests into your builder and validate the emitted XML with lightSAML's own validator (or xmlsec1). Capture fixture requests/responses from a real SP — this catches canonicalisation, namespace, and timestamp bugs far faster than manual testing.
src/Builder and src/Store end-to-end, and budget for maintaining a fork. Prefer depending on lightsaml/lightsaml directly if you only need a narrow subset.NotBefore/NotOnOrAfter are off. Keep servers on NTP; keep assertion lifetimes short (2–5 minutes); account for Conditions vs SubjectConfirmationData having separate timestamps.Destination must exactly equal the SP's ACS URL; Audience must exactly equal the SP's entity ID. String mismatches (trailing slash, http vs https) are the #1 source of "invalid response" errors.InResponseTo. If you populate it, you must have a matching stored request ID, and that record must be single-use. If you skip it, some SPs reject the response.SAMLRequest by hand: base64-decode, then raw-inflate. openssl base64 -d | zlib-flate -uncompress or a small PHP helper.openssl pkcs12 -in ... -nodes). Make sure the cert in your metadata matches the cert you actually sign with — rotating one without the other breaks trust silently.xmlseclibs/xmlsec canonicalisation. Whitespace or attribute-order differences introduced by templating engines (Blade, Twig) or DOMDocument reloads can invalidate signatures. Build the XML with lightSAML's own objects rather than string templates.Store implementations to back state with Redis/DB, and swap the credential/signing providers to use a KMS or vault-issued key. Do this rather than editing the vendor code.How can I help you explore Laravel packages today?