npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hmp-global/payload-cas-auth

v0.2.2

Published

CAS SSO authentication plugin for Payload CMS v3 + Next.js 16.

Readme

@hmp-global/payload-cas-auth

CAS SSO authentication plugin for Payload CMS v3 + Next.js 16.

  • Registers CAS login/callback/logout as Payload API endpoints (no per-app route files needed)
  • JWT session management (signed with your own secret)
  • Role-based access control derived from CAS attributes
  • Seamless Payload admin panel bridging — users go straight to /admin after CAS login
  • Configurable CAS attribute → application role mapping
  • Optional middleware role gates for path-level access control
  • Dev bypass mode with injected role for local development

Installation

npm install @hmp-global/payload-cas-auth
# or
pnpm add @hmp-global/payload-cas-auth

Peer dependencies (install if not already present): next >= 16, payload >= 3, jose >= 5


Publishing a new version

  1. Bump the version in package.json
  2. Add an NPM_TOKEN secret to the GitHub repo (create one at npmjs.com → Access Tokens → Automation token)
  3. Create a GitHub Release — the publish.yml workflow builds and publishes to npm automatically

Setup — two files

1. payload.config.ts

import { buildConfig } from 'payload'
import { casAuthPlugin } from '@hmp-global/payload-cas-auth'

export default buildConfig({
  // ...
  plugins: [
    casAuthPlugin({
      casBaseUrl:    process.env.CAS_BASE_URL!,
      sessionSecret: process.env.CAS_SESSION_SECRET!,
      publicBaseUrl: process.env.PUBLIC_BASE_URL,     // e.g. https://app.example.com
      postLoginRedirectPath: '/admin',                // fallback when no returnTo is present
      enabled:       process.env.CAS_ENABLED !== 'false',
      roleAttribute: 'department',
      roleGroups: {
        engineering: 'developer',
        marketing:   'marketer',
        admin:       'admin',
      },
    }),
  ],
})

This registers four endpoints automatically: | Method | Path | Purpose | |--------|------|---------| | GET | /api/auth/cas/login | Redirects to CAS server | | GET | /api/auth/cas/callback | Validates ticket, sets session cookie | | GET | /api/auth/logout | Clears session cookie | | GET | /api/auth/admin-session | Bridges CAS session → Payload admin cookie |

2. src/middleware.ts

import { createCasMiddleware } from '@hmp-global/payload-cas-auth/middleware'

export const { middleware, config } = createCasMiddleware({
  casBaseUrl:    process.env.CAS_BASE_URL!,
  sessionSecret: process.env.CAS_SESSION_SECRET!,
  publicBaseUrl: process.env.PUBLIC_BASE_URL,
  enabled:       process.env.CAS_ENABLED !== 'false',
  devRole:       process.env.DEV_ROLE ?? 'admin',

  // Paths that require CAS login (default: ['/dashboard', '/chat', '/admin'])
  protectedPaths: ['/dashboard', '/chat', '/admin'],

  // Optional path gates for roles resolved during CAS callback
  roleGates: [
    { path: '/dashboard/cdn', allowedRoles: ['admin', 'developer'], redirectTo: '/dashboard' },
    { path: '/admin', allowedRoles: ['admin', 'developer'], redirectTo: '/dashboard' },
  ],
})

Environment variables

| Variable | Required | Description | |----------|----------|-------------| | CAS_BASE_URL | ✅ | CAS server base URL, e.g. https://login.example.com/cas | | CAS_SESSION_SECRET | ✅ | Secret for signing session JWTs (32+ random chars) | | PUBLIC_BASE_URL | Recommended | Public hostname for CAS redirect URIs | | CAS_ENABLED | — | Set to false to bypass CAS in dev. Default: true | | DEV_ROLE | — | Role to inject when CAS_ENABLED=false. Default: admin |


Login redirects

The middleware preserves the originally requested protected path by passing a safe internal returnTo path through /api/auth/cas/login and /api/auth/cas/callback. For example, visiting /admin redirects through CAS and returns to /admin after the CAS session cookie is set.

If a login starts without a returnTo value, the callback redirects to postLoginRedirectPath. That option defaults to /dashboard for backward compatibility. Set it to /admin for Payload-admin-only apps.

External redirect targets are ignored; only internal paths that start with a single / are accepted.


Role configuration

The plugin reads a CAS attribute to determine each user's role:

casAuthPlugin({
  // ...
  roleAttribute: 'department',   // CAS attribute name (default: 'role')
  roleGroups: {
    engineering:  'developer',
    development:  'developer',
    marketing:    'marketer',
    content:      'marketer',
    'it-admin':   'admin',
  },
  defaultRole: 'viewer',
})

If the role-mappings collection is enabled (default), the CAS callback checks cas-role-mappings in Payload first, then falls back to roleGroups.

Role gates

Use middleware roleGates to restrict paths by resolved role:

roleGates: [
  { path: '/dashboard/cdn', allowedRoles: ['admin', 'developer'], redirectTo: '/dashboard' },
  { path: '/admin', allowedRoles: ['admin'], redirectTo: '/dashboard' },
]

Using the role in your app

The middleware injects the resolved role as an x-user-role request header:

// In a server component or API route:
import { headers } from 'next/headers'
import { ROLE_HEADER } from '@hmp-global/payload-cas-auth'

const reqHeaders = await headers()
const role = reqHeaders.get(ROLE_HEADER) ?? 'unknown'
const canSeeCdn = role === 'admin' || role === 'developer'

Admin panel auto-login

When a CAS-authenticated user visits /admin:

  1. Middleware sends unauthenticated /admin visits through CAS with returnTo=/admin
  2. CAS callback sets the CAS session cookie and redirects back to /admin
  3. Middleware sees the _cas_admin marker cookie is absent → routes to /api/auth/admin-session
  4. The bridge finds/creates their Payload user account by email
  5. Syncs a deterministic server-side password (HMAC(email, sessionSecret))
  6. Calls payload.login() with that password → gets a verified Payload JWT
  7. Sets payload-token + _cas_admin cookies → redirects to /admin
  8. User lands directly in the admin panel — no separate login prompt

For apps served from multiple subdomains, set cookies.domain to the shared parent domain, such as .example.com. This lets the CAS session, Payload admin token, and admin marker cookie work across tenant subdomains.

Existing Payload accounts are matched by email. If you already created an admin account via the Payload email/password flow, the bridge will find it and update its password to the deterministic value (the original password is replaced — Payload admin login is intentionally replaced by CAS).

To limit admin access to specific CAS roles, add an /admin roleGate in middleware.


Finding your CAS attribute name

After first login with CAS_ENABLED=true, the server logs will print:

[cas-auth] login user=jsmith [email protected] role=unknown attrs={"department":"Engineering","uid":"jsmith"}

If role=unknown, look at the attrs keys and set roleAttribute to the right key (e.g. department). Then add the values to devGroups/marketingGroups etc.