next-cas-client
v1.3.1
Published
A CAS client built for Next.js to authenticate, validate tickets, and provide session management to a CAS server
Downloads
894
Maintainers
Readme
next-cas-client
Maintained by the University of Hawaiʻi.
Designed for Next.js, next-cas-client
serves as an API platform to interact with a CAS server to authenticate, validate tickets, and provide session management (powered by iron-session).
Currently supports CAS 2.0, CAS 3.0 and SAML 1.1 service validation methods.
Contributions to this repo to support more validation protocols is highly encouraged! (See contributing)
Table of Contents
Getting Started
1. Installation
# Using npm:
npm i next-cas-client
# Using pnpm:
pnpm add next-cas-client
# Using yarn:
yarn add next-cas-client
2. Set Environment Variables
.env
:
NEXT_PUBLIC_BASE_URL=https://example.com
NEXT_PUBLIC_CAS_URL=https://cas.example.com/cas
NEXT_CAS_CLIENT_SAML_TOLERANCE=18000
NEXT_PUBLIC_BASE_URL
(Required): The URL to redirect to after logging-in/outNEXT_PUBLIC_CAS_URL
(Required): The URL prefix of your CAS serverNEXT_CAS_CLIENT_SAML_TOLERANCE
(Optional): The tolerance in milliseconds for drifting clocks when validating SAML tickets. Only applies to SAML 1.1 validation. Defaults to 1000 milliseconds.
.env.local
:
NEXT_CAS_CLIENT_SECRET=GenerateA32CharacterLongPassword
NEXT_CAS_CLIENT_SECRET
(Required): The secret used to encrypt/decrypt the session stored as a cookie. It must be greater than 32 characters long. Use https://1password.com/password-generator to generate a password.
3. Add API Route
App Router: app/api/cas/[client]/route.ts
:
import { ValidatorProtocol } from 'next-cas-client';
import { handleAuth } from 'next-cas-client/app';
export const GET = handleAuth({ validator: ValidatorProtocol.CAS30 });
Page Router: pages/api/cas/[client].ts
:
import { ValidatorProtocol } from 'next-cas-client';
import { handleAuth } from 'next-cas-client/pages';
export default handleAuth({ validator: ValidatorProtocol.CAS30 });
handleAuth() Options:
validator
(Required): The ValidatorProtocol enumValidatorProtocol.CAS20
for CAS 2.0 service validationValidatorProtocol.CAS20
for CAS 3.0 service validationValidatorProtocol.SAML11
for SAML 1.1 validation
loadUser
(Optional): Function to redefine the user object stored in session.Parameters:
casUser: CasUser
Returns:
any | Promise<any>
If a
loadUser
function is not passed in, the stored user in session defaults to a object of typeCasUser
:type CasUser = { user: string; attributes: record<string, string | string[]>; };
Example: Using
loadUser
to redefine the user and store authorization roles:import { handleAuth, ValidatorProtocol } from 'next-cas-client'; async function loadUser(casUser: CasUser) { const user = { uid: casUser.user name: casUser.attributes.name, email: casUser.attributes.email, roles: [] } await setRoles(user); // Makes API call(s) to retrieve and set the user's roles return user; } export default handleAuth({ loadUser, validator: ValidatorProtocol.SAML11 });
Usage
login(): void
(Client-Side Only)
Visits the CAS login page.
'use client';
import { login } from 'next-cas-client';
<button onClick={() => login()}>Login</button>;
login() Options:
renew
(Optional): Boolean,true
to disallow SSO. Defaults tofalse
.
logout(): void
(Client-Side Only)
Visits the CAS logout page.
'use client';
import { login } from 'next-cas-client';
<button onClick={() => logout()}>Logout</button>;
logout() Options:
enableSLO
(Optional): Boolean,true
to enable SLO (Single Logout). Destroys current SSO session. Defaults tofalse
.
getCurrentUser<T>(): Promise<T = CasUser | null>
(Server-Side Only)
Gets the current user. Returns null
if no user is logged-in.
App Router:
import { getCurrentUser } from 'next-cas-client/app';
const currentUser = await getCurrentUser();
Page Router:
import { getCurrentUser } from 'next-cas-client/pages';
export const getServerSideProps = (async (context) => {
return { props: { currentUser: await getCurrentUser(context) } };
}) satisfies GetServerSideProps<{ currentUser: CasUser | null }>;
Returns an object of type CasUser
by default. Define the generic type of getCurrentUser()
if you used the loadUser
option in handleAuth()
. The type should match the return of the loadUser
function you defined.
Example:
type User = { uid: string; name: string; email: string; roles: string[]; }; const currentUser = await getCurrentUser<User>();
isLoggedIn(): Promise<boolean>
(Server-Side Only)
Returns true
if a user is logged-in.
App Router:
import { getCurrentUser } from 'next-cas-client/app';
const isLoggedIn = await isLoggedIn();
Page Router:
import { getCurrentUser } from 'next-cas-client/pages';
export const getServerSideProps = (async (context) => {
return { props: { isLoggedIn: await isLoggedIn(context) } };
}) satisfies GetServerSideProps<{ isLoggedIn: boolean }>;
Examples
A fully functional demo is available using Docker.
To start:
- Clone or download this repo
- Change directory to
/examples
- Execute
docker compose up
. A CAS server for demo purposes will launch at https://localhost:8443/cas - Change directory to
/app-router
or/pages-router
depending on the router you would like to view the example for - Execute
npm install
thennpm run dev
. The Next.js example app will be available at http://localhost:3000
Note: You may encounter a NET:ERR_CERT_INVALID
error in your browser when attempting to visit the CAS login page. Bypass the error by trusting the page. The browser is attempting to protect you from visiting a suspicious secure site at https://localhost.
Contributing
Please open a new issue before creating a PR. This project is currently focusing on supporting more ticket validation protocols. Any other issues and PRs created out of this scope may be rejected.
For more information on how to contribute, view here.
FAQ
Is there support for other React frameworks like Vite and Remix?
No, not at this moment.
How do I use getCurrentUser()
and isLoggedIn()
in a client component?
It is recommended to use those functions inside a server component then pass them as props into a client component.
How do I resolve the error when importing getCurrentUser()
and isLoggedIn()
from 'next-cas-client/app'
or 'next-cas-client/pages'
?
In your project's tsconfig.json
, set compilerOptions.moduleResolution
to "bundler".