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

Shibboleth Bundle Laravel Package

unicesil/shibboleth-bundle

Symfony bundle that integrates Shibboleth single sign-on into your app. It reads Shibboleth server variables to identify users, feeds them into Symfony's security layer, and simplifies protecting routes with SSO-based authentication.

View on GitHub
Deep Wiki
Context7

Getting Started

The first thing to internalize: this is a Symfony bundle, not a Laravel package. unicesil/shibboleth-bundle wires a Symfony security authenticator to the attributes that Apache's mod_shib injects into $_SERVER (e.g. REMOTE_USER, Shib-Identity-Provider, mail, eduPersonPrincipalName). There is no service provider, no facade, and composer require in a Laravel app gets you nothing but dead weight. The right move for a Laravel team is to treat it as a reference implementation of the Shibboleth attribute contract and port the pattern.

Minimal path to first value:

  1. Get the SP side working outside Laravel first. Configure mod_shib (or nginx + shibd) with your federation metadata, protect /sp-test, and confirm the attributes arrive: var_dump($_SERVER) for keys starting with Shib-, HTTP_SHIB_, or REDIRECT_.
  2. Read three files in the bundle before writing anything: the DI service definition (Resources/config/services.xml or config/services.yaml), the guard authenticator class, and the fake/test authenticator. Those tell you the attribute map, the user-resolution strategy, and how they fake auth in dev.
  3. Translate the config into config/shibboleth.php — a attributes map (uid, mail, displayName, affiliation), a user_model, and a fake flag.
  4. First use case: protect /dashboard with a shibboleth guard that resolves-or-creates an App\Models\User by eduPersonPrincipalName, then Auth::login().

The quickest possible version in Laravel, no custom guard class:

// AppServiceProvider::boot()
Auth::viaRequest('shibboleth', function (Request $request) {
    $eppn = $request->server('REMOTE_USER') ?? $request->server('HTTP_EPPN');
    if (! $eppn) {
        return null;
    }
    return User::firstOrCreate(
        ['eppn' => $eppn],
        ['name' => $request->server('HTTP_DISPLAYNAME', $eppn),
         'email' => $request->server('HTTP_MAIL')]
    );
});

Register it in config/auth.php as a guard with 'driver' => 'shibboleth'. That's 90% of what the bundle gives you.

Implementation Patterns

Resolve users by an immutable identifier, not email. Email changes; eppn is scoped to an IdP and stable, and persistent-id (or Shibboleth-persistent-id) is stabler still. Store both eppn and shib_identity_provider on the user row so you can survive multi-IdP federations and audit which IdP vouched for whom. firstOrCreate on eppn is the workhorse; sync mail and displayName on each login rather than treating them as primary keys.

Session persistence is the pattern the bundle uses, and you should copy it. The Shib-* headers are only present on requests routed through the Shibboleth-protected location. If you read them on every request you'll get null on AJAX calls, asset endpoints, and anything the SP doesn't protect. The bundle's authenticator resolves the user once, then relies on the Symfony session token. In Laravel: persist user_id in the session in your guard's user() method, and only call the header-reading path when the session is empty.

public function user()
{
    if ($this->user !== null) return $this->user;
    if ($id = $this->session->get('shib_user_id')) {
        return $this->user = $this->provider->retrieveById($id);
    }
    // fall through to header-based resolution, then $this->session->put(...)
}

Keep /Shibboleth.sso/* out of Laravel. The SP handler endpoints (/Shibboleth.sso/Login, /Shibboleth.sso/SAML2/POST, /Shibboleth.sso/Logout) are served by mod_shib before PHP. Don't add routes; do add an exclusion in your web-server config so Laravel's front controller never sees them, and make sure APP_URL/TrustProxies reflect the external scheme so redirects after SP-initiated login don't bounce to http://.

Logout is a two-step dance. Auth::logout() only kills the Laravel session — the user is still authenticated at the IdP and the next request re-authenticates them instantly. The pattern is: destroy the local session, then redirect to /Shibboleth.sso/Logout?return=<urlencoded app home> for SP-initiated single logout. Give users a distinct "sign out everywhere" affordance if your IdP doesn't do SLO reliably, because many don't.

Drive authorization off federation attributes. eduPersonAffiliation (student, faculty, staff, member) and isMemberOf are the natural inputs for role assignment. Map them in a small ShibbolethAttributeMapper class invoked on user creation/update — that's the extension point the bundle exposes via its user factory, and it's the piece you'll actually customize. Don't put the mapping inline in the guard; you'll want to unit test it against fixture attribute arrays.

Fake mode is essential and must be fenced. The bundle ships a test authenticator that bypasses Shibboleth. Mirror it: a SHIB_FAKE=true env var that swaps the attribute source for a hardcoded array, gated on app()->environment('local', 'testing') and an explicit config flag. In tests, $this->withServerVariables(['REMOTE_USER' => '[email protected]', 'HTTP_MAIL' => '...']) covers the real path without any fake at all — prefer that.

IdP-initiated flows. If your IdP supports unsolicited assertions, the first request lands on your ACS with no session. Your login route should tolerate arriving already-authenticated and just redirect to the intended URL. Use session()->put('url.intended', ...) in your authenticate middleware so the post-login redirect behaves like normal Laravel.

Gotchas and Tips

Header spoofing is the number-one security risk, full stop. Shib-* and REMOTE_USER are just request headers. If a client can reach PHP directly — bypassing mod_shib, or through a reverse proxy that forwards client-supplied Shib- headers — they can impersonate anyone by sending Shib-Identity-Provider: https://idp.example.edu and REMOTE_USER: admin. Mitigate at the edge: RequestHeader unset Shib-Identity-Provider (and the whole family) in Apache on the front vhost, or proxy_set_header Shib-Identity-Provider ""; in nginx, then let mod_shib set them. Also bind shibd to localhost only, and never expose port 80 to the SP directly. This is a config concern the bundle cannot solve for you.

$_SERVER key naming is inconsistent and will bite you. Depending on ShibUseHeaders vs ShibUseEnvironment and whether the request passed through a rewrite, an attribute may appear as mail, HTTP_MAIL, REDIRECT_mail, or REDIRECT_HTTP_MAIL. Write one attribute(string $name) helper that probes all four variants, and use it everywhere. Add a debug route in local that dumps filtered $_SERVER so you can see which spelling your deployment uses.

REMOTE_USER is often empty even when Shibboleth works. Some SP configurations only populate the Shib-* attributes and leave REMOTE_USER unset. Don't make it the sole resolution key; fall back through eppn → persistent-id → uid with a clear precedence order, and log which one matched on first login.

Session lifetime mismatch. The Shibboleth session (governed by Shibboleth.sso/Session and IdP SessionNotOnOrAfter) frequently expires before or after the Laravel session. Symptom: user sees the app but every Shibboleth-protected sub-resource 302s to the IdP, or the reverse — a 30-minute SESSION_LIFETIME leaves users locally logged in with a dead IdP session. Set SESSION_LIFETIME to something at or below the IdP's assertion lifetime, and listen for Shib-Session-Index disappearing to force re-auth.

Attribute names are per-federation, not universal. Some IdPs send eduPersonPrincipalName as HTTP_EPPN, others as HTTP_EDUPERSONPRINCIPALNAME, and mail vs HTTP_MAIL. The bundle handles this with an attribute map — do the same, and treat the map as deployment configuration, not code. If you hardcode HTTP_MAIL you will ship a bug that only appears at the customer's IdP.

Fake mode in production is a silent auth bypass. The bundle's testing authenticator is a footgun if the enable condition is loose (if (!isset($_SERVER['REMOTE_USER']))). Gate it on an env var that defaults to false and add a boot-time abort(500) if SHIB_FAKE is truthy while app()->isProduction(). Also assert in your deploy checklist.

Don't route-guard the ACS. A common mistake is adding auth middleware globally; the assertion consumer endpoint is where the login happens, so guarding it produces a redirect loop. Apply auth:shibboleth per-route-group.

On the package itself: 3 stars, 0 dependents, and a narrow scope mean you should pin an exact version or vendor the pattern. Expect to read source rather than docs, and expect no response on issues. The valuable, portable parts are the attribute-map config shape, the fake authenticator, and the user-factory extension point — those port to Laravel in an afternoon. The Symfony DI wiring does not.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log