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

Cas Laravel Package

subfission/cas

Laravel package for CAS (Central Authentication Service) single sign-on. Provides CAS authentication, middleware, user retrieval, and logout integration for Laravel apps, making it easy to add institutional SSO support.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the wrapper

    composer require subfission/cas
    

    On Laravel 5.5+ the Subfission\Cas\CasServiceProvider and Cas facade are auto-discovered. On older versions, add the provider and 'Cas' => Subfission\Cas\Facades\Cas::class to config/app.php.

  2. Publish and fill the config

    php artisan vendor:publish --provider="Subfission\Cas\CasServiceProvider"
    

    config/cas.php maps almost 1:1 onto phpCAS options — the ones that matter first:

    'hostname'    => env('CAS_HOSTNAME', 'cas.example.edu'),
    'port'        => env('CAS_PORT', 443),
    'context'     => env('CAS_CONTEXT', '/cas'),
    'cas_version' => env('CAS_VERSION', '2.0'), // CAS_VERSION_2_0 / CAS_VERSION_3_0
    'certificate' => env('CAS_CERTIFICATE', ''), // path to CA/chain used to verify the CAS server
    

    The service URL that phpCAS sends to the CAS server is derived from the incoming request, so APP_URL and your proxy/trusted-proxy config matter as much as the CAS host settings.

  3. Register the middleware aliases (Laravel ≤10 in app/Http/Kernel.php, Laravel 11+ in bootstrap/app.php):

    'cas.auth'  => \Subfission\Cas\Middleware\Authenticate::class,
    'cas.guest' => \Subfission\Cas\Middleware\RedirectIfAuthenticated::class,
    
  4. First use case — protect a dashboard and dump what the CAS server actually returned:

    Route::middleware('cas.auth')->group(function () {
        Route::get('/dashboard', function () {
            // Cas::user() is the CAS principal (username string)
            // Cas::getAttributes() is the raw attribute array from the CAS response
            dd(Cas::user(), Cas::getAttributes());
        });
    });
    

    Once you can see the attribute names your CAS server releases (uid, mail, displayName, memberOf, …), you know exactly what you can key your local user records on.

Implementation Patterns

Wrap the protected area, not individual routes. Typical shape:

Route::middleware('cas.guest')->get('/login', fn () => view('login'));

Route::middleware('cas.auth')->group(function () {
    Route::get('/', [DashboardController::class, 'index']);
    Route::get('/profile', [ProfileController::class, 'show']);
});

cas.auth calls Cas::check() and, if there is no valid CAS session, triggers Cas::authenticate() — a redirect to the CAS login page with a ?ticket= round-trip handled transparently on the way back. cas.guest is the inverse: already-authenticated users get bounced away from your login page.

Bridge CAS identity to a Laravel user. This is the single most important pattern: subfission/cas is not a Laravel guard/provider, so Auth::user() stays null until you log someone in yourself.

// app/Http/Middleware/SyncCasUser.php
public function handle($request, Closure $next)
{
    if (Cas::check() && ! Auth::check()) {
        $user = User::firstOrCreate(
            ['cas_uid' => Cas::user()],
            [
                'name'  => Cas::getAttribute('displayName') ?? Cas::user(),
                'email' => Cas::getAttribute('mail'),
            ]
        );

        Auth::login($user, remember: true);
    }

    return $next($request);
}

Register it after cas.auth so the CAS session already exists, and run it on every request (or in a Login event listener) so attribute changes on the CAS side propagate. Keeping a local users row is also how you attach roles, relations, and audit data — CAS only gives you identity, not authorization.

Read attributes defensively. Servers differ wildly in what they release, and attributes for an SSO session validated earlier may be missing if the release policy changed. Prefer:

Cas::hasAttribute('mail') ? Cas::getAttribute('mail') : null;
// and always keep a fallback to Cas::user()

Logout. CAS logout is global single logout — it invalidates the SSO session across every application on that CAS server. Do local cleanup first, then hand off:

public function logout(Request $request)
{
    Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return redirect()->away(Cas::logout()); // or Cas::logout() if it returns a redirect
}

Consider a confirmation page: users rarely expect "log out of this app" to log them out of their email and intranet too.

Single sign-on across apps comes for free: the second Laravel app on the same CAS server hits its cas.auth route, Cas::check() finds an existing CAS session cookie, and the user never sees a login form. Test this explicitly — it's the whole point of CAS and the thing most likely to break if your service URLs are inconsistent.

Testing. Because the session is the source of truth, feature tests can stub the CAS session instead of doing redirect dances — set the CAS session keys the package uses after login, or bind a mock Cas instance into the container for middleware tests. Reserve real browser tests (Dusk) for the actual redirect/ticket flow.

Gotchas and Tips

  • Cas::user() ≠ Auth::user(). The most common first bug report. The package stores the CAS principal in the session and exposes it through the facade; Laravel's auth system is untouched. You must call Auth::login() yourself (see above).
  • Service URL mismatches. phpCAS computes the service URL from the request. Behind a load balancer or TLS-terminating proxy this becomes http:// or an internal hostname, and the CAS server rejects the ticket (INVALID_TICKET / "service not authorized"). Fix with correct TrustProxies configuration and APP_URL, not by hacking the config.
  • TLS verification. certificate must point at the CA/chain that signed the CAS server cert. Empty or wrong values produce opaque phpCAS failures. Use the debug flag in the config (and phpCAS's debug output) while bringing a new environment up — then turn it off.
  • Session interaction. phpCAS manages its own session state and can conflict with Laravel's session handling. Symptoms: lost cas_user between requests, "headers already sent" warnings, or a login loop. Check that CAS middleware runs inside the web group and that your session driver behaves under the redirect round-trip.
  • Login loops usually mean Cas::check() never returns true after the redirect — bad service URL, expired/replayed ticket, or a session that isn't persisting cookies. Log the return URL and ticket parameter on the callback route.
  • Tickets are one-time. Never cache or replay a URL containing ?ticket=. Don't put CAS-protected URLs behind aggressive full-page caching.
  • Middleware ordering. cas.auth must appear before any middleware that assumes Auth::check(). If you add your SyncCasUser middleware, it must come after cas.auth in the group.
  • Never call CAS from CLI or queues. Cas::check() / Cas::authenticate() are request/redirect-oriented and depend on session and HTTP context. In jobs, look up the local User by cas_uid instead.
  • Config caching. Run php artisan config:cache after changing CAS settings; stale cached config is a classic "I changed the hostname and nothing happened."
  • This package is archived. It's a thin wrapper and still works, but there will be no fixes for new Laravel/PHP versions or phpCAS CVEs. Pin the version, keep the usage surface small (a handful of Cas:: calls behind one service class), and plan an exit: either depend on apereo/phpcas directly inside a custom middleware, or implement a proper Laravel guard so the swap is localized.
  • Extension point to lean on: because the wrapper is thin, put all CAS interaction behind a single CasAuthenticator/CasUserResolver class rather than sprinkling Cas:: calls through controllers. When you migrate off the archived package — or need a second identity provider — you change one class instead of forty.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
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