Skip to content

uhawaii-system-its-ti-iam/next-cas-client

Repository files navigation

next-cas-client CI Coverage GitHub license

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/out
  • NEXT_PUBLIC_CAS_URL (Required): The URL prefix of your CAS server
  • NEXT_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 enum

    • ValidatorProtocol.CAS20 for CAS 2.0 service validation
    • ValidatorProtocol.CAS20 for CAS 3.0 service validation
    • ValidatorProtocol.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 type CasUser:

      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 to false.

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 to false.

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:

  1. Clone or download this repo
  2. Change directory to /examples
  3. Execute docker compose up. A CAS server for demo purposes will launch at https://localhost:8443/cas
  4. Change directory to /app-router or /pages-router depending on the router you would like to view the example for
  5. Execute npm install then npm 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".

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published