-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
45 changed files
with
9,853 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
NEXT_PUBLIC_BASE_URL=https://example.com | ||
NEXT_PUBLIC_CAS_URL=https://cas.example.com/cas | ||
NEXT_CAS_CLIENT_SECRET=GenerateA32CharacterLongPassword | ||
NEXT_CAS_CLIENT_SAML_TOLERANCE=18000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Issue Link | ||
|
||
[Issue](Insert Link Here) | ||
|
||
# Checklist | ||
|
||
- [ ] Tests Passed |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
- run: npm install | ||
- run: npm run lint | ||
- run: npm run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
name: Coverage | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
generate-badges: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
- run: npm install | ||
- run: npm run test:coverage | ||
- run: git checkout badges | ||
- uses: jpb06/jest-badges-action@latest | ||
with: | ||
target-branch: badges | ||
- run: git push origin badges |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/dist | ||
/node_modules | ||
/coverage | ||
|
||
.env | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/src | ||
/node_modules | ||
/coverage | ||
|
||
.env | ||
.DS_Store | ||
|
||
tsconfig.json | ||
tsup.config.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
dist | ||
coverage | ||
node_modules | ||
*.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"printWidth": 120, | ||
"trailingComma": "none", | ||
"tabWidth": 4, | ||
"semi": true, | ||
"singleQuote": true | ||
} |
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
@uhawaii-system-its-ti-iam/uh-groupings-team |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Contributing to next-cas-client | ||
|
||
Thanks for taking the time to contribute to `next-cas-client`! This document will help get you started and provide guidelines on how to add a new ticket validator. | ||
|
||
## Getting Started | ||
|
||
### Setup | ||
|
||
1. Install LTS version of Node.js | ||
2. Install the Prettier extension in your IDE. | ||
3. Fork and clone the repo | ||
4. Set remote upstream in git | ||
5. Run `npm install` to install dependencies | ||
|
||
### Working on an Issue | ||
|
||
1. Create/select an issue | ||
2. Create a new branch | ||
3. Make changes in the /src folder | ||
4. Write tests in the /test folder (Ensuring 100% code coverage!) | ||
5. Create a pull request linked to the issue | ||
|
||
### Testing Changes Locally | ||
|
||
1. Run `npm run build` to build the project | ||
2. Run `npm link` | ||
3. Run `npm link next-cas-client` in another project that imports `next-cas-client` as a dependency | ||
4. Now test the changes | ||
|
||
### Running Tests | ||
|
||
1. Run `npm run test` to run the Jest tests | ||
2. Run `npm run test:coverage` to view the test coverage | ||
|
||
## Adding a New Validator | ||
|
||
1. Create a new `.ts` file in src/lib/validators. | ||
2. Create a class that implements the `Validator` interface. _Note that you cannot extend a class because Next.js prevents the use of classes in Server components._ | ||
3. Update the ValidatorProtocol enum and ValidatorFactory.getValidator() method in src/lib/validators/validator.ts. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,211 @@ | ||
# next-cas-client | ||
# next-cas-client [![CI](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/actions/workflows/ci.yml/badge.svg)](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/actions/workflows/ci.yml) [![GitHub license](https://img.shields.io/github/license/uhawaii-system-its-ti-iam/next-cas-client?style=flat)](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/blob/master/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](https://github.com/vvo/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](#contributing))** | ||
|
||
## Table of Contents | ||
|
||
- [Getting Started](#getting-started) | ||
- [Installation](#1-installation) | ||
- [Set Environment Variables](#2-set-environment-variables) | ||
- [Add API Route](#3-add-api-route) | ||
- [Usage](#usage) | ||
- [`login(): void`](#login-void-client-side-only) | ||
- [`logout(): void`](#logout-void-client-side-only) | ||
- [`getCurrentUser<T>(): Promise<T = CasUser | null>`](#getcurrentusert-promiset--casuser--null-server-side-only) | ||
- [`isLoggedIn(): Promise<boolean>`](#isloggedin-promiseboolean-server-side-only) | ||
- [Examples](#examples) | ||
- [Contributing](#contributing) | ||
- [FAQ](#faq) | ||
- [Is there support for other React frameworks like Vite and Remix?](#is-there-support-for-other-react-frameworks-like-vite-and-remix) | ||
- [How do I use `getCurrentUser()` and `isLoggedIn()` in a client component?](#how-do-i-use-getcurrentuser-and-isloggedin-in-a-client-component) | ||
- [How do I resolve the error when importing `login()` and `logout()` from `'next-cas-client/client'`?](#how-do-i-resolve-the-error-when-importing-login-and-logout-from-next-cas-clientclient) | ||
|
||
## Getting Started | ||
|
||
### 1. Installation | ||
|
||
```bash | ||
# 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`: | ||
|
||
```ts | ||
import { handleAuth, ValidatorProtocol } from 'next-cas-client'; | ||
|
||
export const GET = handleAuth({ validator: ValidatorProtocol.SAML11 }); | ||
``` | ||
|
||
**Page Router:** `pages/api/cas/[client].ts`: | ||
|
||
```ts | ||
import { handleAuth, ValidatorProtocol } from 'next-cas-client'; | ||
|
||
export default handleAuth({ validator: ValidatorProtocol.SAML11 }); | ||
``` | ||
|
||
**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`: | ||
```ts | ||
type CasUser = { | ||
user: string; | ||
attributes: record<string, string | string[]>; | ||
}; | ||
``` | ||
- **Example:** Using `loadUser` to redefine the user and store authorization roles: | ||
|
||
```ts | ||
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. | ||
|
||
```jsx | ||
'use client'; | ||
import { login } from 'next-cas-client/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. | ||
|
||
```jsx | ||
'use client'; | ||
import { login } from 'next-cas-client/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. | ||
|
||
```ts | ||
const currentUser = await getCurrentUser(); | ||
``` | ||
|
||
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: | ||
|
||
```ts | ||
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. | ||
|
||
```ts | ||
const isLoggedIn = await isLoggedIn(); | ||
``` | ||
|
||
## Examples | ||
|
||
For the full demo, Docker is required. | ||
|
||
- [App Router](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/blob/main/examples/app-router) | ||
- [Page Router](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/blob/main/examples/page-router) | ||
|
||
## 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](https://github.com/uhawaii-system-its-ti-iam/next-cas-client/blob/main/CONTRIBUTING.md). | ||
|
||
## 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 `login()` and `logout()` from `'next-cas-client/client'`? | ||
|
||
In your project's `tsconfig.json`, set `compilerOptions.moduleResolution` to "bundler". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = jest.genMockFromModule('iron-session'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = jest.genMockFromModule('next/headers'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = jest.genMockFromModule('next/navigation'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import eslint from '@eslint/js'; | ||
import tseslint from 'typescript-eslint'; | ||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; | ||
|
||
export default tseslint.config({ | ||
extends: [eslint.configs.recommended, eslintPluginPrettierRecommended, ...tseslint.configs.recommended], | ||
ignores: ['**/dist/*'], | ||
rules: { | ||
'prettier/prettier': ['error', { endOfLine: 'auto' }], | ||
'@typescript-eslint/no-explicit-any': 'off' | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { Config } from 'jest'; | ||
|
||
const config: Config = { | ||
coverageReporters: ['json-summary', 'text', 'html'], | ||
collectCoverageFrom: ['./src/**/*.ts*'], | ||
rootDir: './', | ||
moduleDirectories: ['node_modules', '<rootDir>'], | ||
moduleNameMapper: { | ||
'^@/(.*)$': '<rootDir>/src/$1' | ||
}, | ||
setupFilesAfterEnv: ['<rootDir>/test/setup-jest.ts'], | ||
testEnvironment: 'jsdom', | ||
testEnvironmentOptions: { | ||
customExportConditions: [] | ||
}, | ||
transform: { | ||
'^.+\\.(t|j)sx?$': '@swc/jest' | ||
} | ||
}; | ||
|
||
export default config; |
Oops, something went wrong.