yraiso/casauth-bundle
A Symfony bundle for integrating CAS single sign-on authentication into your app. It simplifies CAS login and logout, service ticket validation, and authenticated user handling through configurable server settings. Designed to secure routes and streamline CAS-based access control.
First, the framework mismatch. yraiso/casauth-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/cas.php, no auto-discovery. If you're working in Laravel, composer require will install it and then nothing will happen — you'd be better served by apereo/phpcas directly behind a custom guard, or a Laravel-native CAS package. Treat this repo as a reference implementation of the CAS handshake you can port. The rest of this assessment assumes you're actually in a Symfony app (or willing to use the bundle's CAS client standalone).
Minimal path in Symfony:
composer require yraiso/casauth-bundle
config/bundles.php after install:Yraiso\CasAuthBundle\CasAuthBundle::class => ['all' => true],
Confirm the namespace from vendor/yraiso/casauth-bundle/composer.json → autoload.psr-4 before copying that line — with 2 stars, don't assume a recipe exists.
Publish/inspect the config. Run bin/console config:dump-reference <root_key> (or read DependencyInjection/Configuration.php directly). This is the single most important file to read first — it tells you the real option names for CAS host, port, context path, service base URL, and validation strategy. The README on a 2-star bundle is usually behind the code.
Wire the firewall in config/packages/security.yaml (see patterns below).
Point it at a CAS server you can actually reach. The first use case is always the same: gate an internal admin or intranet app behind a corporate CAS server, making CAS the sole identity source and mapping returned attributes to Symfony roles.
Where to look, in order: README → DependencyInjection/Configuration.php → the listener/authenticator class → Resources/config/services.*. The listener is where the actual phpCAS::forceAuthentication() call lives.
Firewall + authenticator. If the bundle ships a Guard authenticator (likely, given the 5.4 floor), wrap or adapt it into custom_authenticators:
# config/packages/security.yaml
security:
providers:
cas:
id: cas.user_provider # from the bundle, if it ships one
firewalls:
main:
pattern: ^/
provider: cas
custom_authenticators:
- App\Security\CasAuthenticator
logout:
path: /logout
target: /
If the bundle only registers a listener (the older style), keep the listener and let it populate the token — but plan to write a 20-line AbstractAuthenticator yourself, because Guard is gone in Symfony 6+.
JIT user provisioning. The most common production pattern: CAS returns a login + attributes, you look up or create a local User row, then issue the token. This keeps your authorization logic (roles, teams, flags) in your own database instead of scattering CAS attribute checks through the app:
$user = $this->userRepository->findOrCreateFromCas(
uid: $attributes['uid'],
email: $attributes['mail'],
displayName: $attributes['displayName'],
);
Attribute → role mapping. Map CAS groups to Symfony roles in one place (a small RoleMapper service driven by a config array), not inline in the authenticator. CAS servers return department/group attributes in wildly different shapes — memberOf, groups, eduPersonAffiliation — so isolate the parsing.
Attribute capture at login time. Because service-ticket validation happens only on the redirect back from CAS, the returned attributes are available once. Persist them onto the user entity (or into the session) during authenticate(), and read from there afterwards. Don't expect to call back into CAS for more attributes later.
Testing. Stand up a stub CAS server (a small container or apereo/cas demo) in CI, or mock the bundle's ticket-validation client. Integration tests that hit a real CAS are flaky by nature — the ticket is single-use and time-bounded.
Bus factor is the real risk. 2 stars, 0 dependents, score 4.67. Pin the exact version in composer.json, commit composer.lock, and be prepared to fork and vendor it if the maintainer disappears — the CAS protocol is stable, so a fork is cheap.
The service URL must match byte-for-byte. CAS compares the service parameter against the URL registered on the server. Behind a load balancer or Docker, Symfony generates http://app:8080/... while CAS expects https://admin.example.com/.... Fix it once, properly:
framework:
trusted_proxies: '%env(TRUSTED_PROXIES)%'
trusted_hosts: ['^admin\.example\.com$']
or force the service base URL explicitly in the bundle config. This is the number-one cause of "CAS redirects back and then rejects the ticket."
Server-to-server reachability. Ticket validation is a back-channel call from your web container to the CAS server (usually 443 or 8443). Symptoms of a blocked path: a long hang, then "ticket not recognized." Test with curl from inside the app container, not from your laptop.
Don't disable TLS verification. phpCAS verifies the CAS server certificate in current versions. If you hit "SSL certificate problem," set the CA bundle path (phpCAS::setCasServerCACert() or the bundle's equivalent) — never setNoCasServerValidation() outside local dev.
The ticket is single-use. After a validation failure, redirecting to CAS again yields a fresh ticket; retrying with the same one always fails. Also strip ?ticket=... from the URL after success — otherwise a browser refresh or back-button triggers a replay-looking request.
Logout that doesn't stick. Symfony's logout destroys the local session, then the next request redirects to CAS, which still has a session, and you're instantly logged back in. You need a second hop to the CAS /logout?service=... endpoint (single logout). The bundle may handle this; verify it, because it's the most commonly broken piece in small CAS bundles.
phpCAS global state. phpCAS uses process-wide globals and its own session handling. Call phpCAS::forceAuthentication() (or the bundle's entry point) once, early in the request — from a listener, never from a Twig template or a controller action that might also be hit by sub-requests. Mixing it with a custom session handler causes intermittent session loss.
Clock skew. CAS tickets carry timestamps; if your app server and CAS server drift more than a minute or two, validation fails with a misleading error. Keep NTP running on both.
Debugging. Turn on phpCAS debug logging — phpCAS::setDebug('/tmp/cas.log') plus verbose mode — and read the log. It reliably tells you which phase failed: ticket acquisition, back-channel validation, or attribute parsing. Guessing without the log wastes hours.
Upgrade path. "Symfony 5.4 minimum" tells you this was written against Guard. Symfony 6 removed Guard and Symfony 7 tightened the authenticator interfaces further. Budget an afternoon to write a thin AbstractAuthenticator that delegates to the bundle's CAS client service; that wrapper is also your escape hatch if the package is abandoned.
How can I help you explore Laravel packages today?