Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for Pages router #22

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 39 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Currently supports CAS 2.0, CAS 3.0 and SAML 1.1 service validation methods.
- [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)
- [How do I resolve the error when importing `getCurrentUser()` and `isLoggedIn()` from `'next-cas-client/app'` or `'next-cas-client/pages'`?](#how-do-i-resolve-the-error-when-importing-getcurrentuser-and-isloggedin-from-next-cas-clientapp-or-next-cas-clientpages)

## Getting Started

Expand Down Expand Up @@ -68,17 +68,19 @@ NEXT_CAS_CLIENT_SECRET=GenerateA32CharacterLongPassword
**App Router:** `app/api/cas/[client]/route.ts`:

```ts
import { handleAuth, ValidatorProtocol } from 'next-cas-client';
import { ValidatorProtocol } from 'next-cas-client';
import { handleAuth } from 'next-cas-client/app';

export const GET = handleAuth({ validator: ValidatorProtocol.SAML11 });
export const GET = handleAuth({ validator: ValidatorProtocol.CAS30 });
```

**Page Router:** `pages/api/cas/[client].ts`:

```ts
import { handleAuth, ValidatorProtocol } from 'next-cas-client';
import { ValidatorProtocol } from 'next-cas-client';
import { handleAuth } from 'next-cas-client/pages';

export default handleAuth({ validator: ValidatorProtocol.SAML11 });
export default handleAuth({ validator: ValidatorProtocol.CAS30 });
```

**handleAuth() Options:**
Expand All @@ -90,7 +92,7 @@ export default handleAuth({ validator: ValidatorProtocol.SAML11 });
- `loadUser` _(Optional)_: Function to redefine the user object stored in session.

- Parameters: `casUser: CasUser`
- Returns: `any | promise<any>`
- 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 = {
Expand Down Expand Up @@ -127,7 +129,7 @@ Visits the CAS login page.
```jsx
'use client';

import { login } from 'next-cas-client/client';
import { login } from 'next-cas-client';

<button onClick={() => login()}>Login</button>;
```
Expand All @@ -143,7 +145,7 @@ Visits the CAS logout page.
```jsx
'use client';

import { login } from 'next-cas-client/client';
import { login } from 'next-cas-client';

<button onClick={() => logout()}>Logout</button>;
```
Expand All @@ -156,10 +158,24 @@ import { login } from 'next-cas-client/client';

Gets the current user. Returns `null` if no user is logged-in.

**App Router:**

```ts
import { getCurrentUser } from 'next-cas-client/app';

const currentUser = await getCurrentUser();
```

**Page Router:**

```ts
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:
Expand All @@ -179,10 +195,24 @@ Returns an object of type `CasUser` by default. Define the generic type of `getC

Returns `true` if a user is logged-in.

**App Router:**

```ts
import { getCurrentUser } from 'next-cas-client/app';

const isLoggedIn = await isLoggedIn();
```

**Page Router:**

```ts
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.
Expand Down Expand Up @@ -213,6 +243,6 @@ No, not at this moment.

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'`?
### 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".
3 changes: 2 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default tseslint.config({
ignores: ['**/dist/*'],
rules: {
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'@typescript-eslint/no-explicit-any': 'off'
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-require-imports': 'off'
}
});
8 changes: 4 additions & 4 deletions examples/app-router/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"clsx": "^2.1.1",
"lucide-react": "^0.453.0",
"next": "14.2.16",
"next-cas-client": "^1.1.1",
"next-cas-client": "^1.2.1",
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.5.4",
Expand Down
3 changes: 2 additions & 1 deletion examples/app-router/src/app/api/cas/[client]/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { handleAuth, ValidatorProtocol } from 'next-cas-client';
import { ValidatorProtocol } from 'next-cas-client';
import { handleAuth } from 'next-cas-client/app';

export const GET = handleAuth({ validator: ValidatorProtocol.CAS30 });
26 changes: 3 additions & 23 deletions examples/app-router/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { getCurrentUser } from 'next-cas-client';
import CasAttributesTable from '@/components/cas-attributes-table';
import { getCurrentUser } from 'next-cas-client/app';

export default async function Home() {
const currentUser = await getCurrentUser();

return (
<div className="container mx-auto flex justify-center">
{currentUser ? (
<Table>
<TableCaption>CAS attributes for {currentUser.user}</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Attribute</TableHead>
<TableHead>Value</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Object.entries(currentUser.attributes).map(([attribute, value]) => (
<TableRow key={attribute}>
<TableCell>{attribute}</TableCell>
<TableCell>{value.toString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<p>No user is logged in.</p>
)}
<CasAttributesTable currentUser={currentUser} />
</div>
);
}
32 changes: 32 additions & 0 deletions examples/app-router/src/components/cas-attributes-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { CasUser } from 'next-cas-client';
import { Table, TableCaption, TableHeader, TableRow, TableHead, TableBody, TableCell } from './ui/table';

const CasAttributesTable = ({ currentUser }: { currentUser: CasUser | null }) => {
return (
<>
{currentUser ? (
<Table>
<TableCaption>CAS attributes for {currentUser.user}</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Attribute</TableHead>
<TableHead>Value</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Object.entries(currentUser.attributes).map(([attribute, value]) => (
<TableRow key={attribute}>
<TableCell>{attribute}</TableCell>
<TableCell>{value.toString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<p>No user is logged in.</p>
)}
</>
);
};

export default CasAttributesTable;
2 changes: 1 addition & 1 deletion examples/app-router/src/components/login-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { login, logout } from 'next-cas-client/client';
import { login, logout } from 'next-cas-client';
import { Button } from './ui/button';

const LoginButton = ({ isLoggedIn }: { isLoggedIn: boolean }) => {
Expand Down
2 changes: 1 addition & 1 deletion examples/app-router/src/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isLoggedIn } from 'next-cas-client';
import { isLoggedIn } from 'next-cas-client/app';
import LoginButton from './login-button';

const Navbar = async () => {
Expand Down
10 changes: 10 additions & 0 deletions examples/pages-router/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
NEXT_PUBLIC_BASE_URL=http://localhost:3000
NEXT_PUBLIC_CAS_URL=https://localhost:8443/cas

# FOR DEMO PURPOSES ONLY
# PLEASE GENERATE YOUR OWN 32 CHARACTER LONG PASSWORD
NEXT_CAS_CLIENT_SECRET=GenerateA32CharacterLongPassword

# FOR DEMO PURPOSES ONLY
# DISABLES CERTIFICATE VERIFICATION TO ALLOW COMMUNICATION WITH https://localhost:8443/cas
NODE_TLS_REJECT_UNAUTHORIZED=0
3 changes: 3 additions & 0 deletions examples/pages-router/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}
36 changes: 36 additions & 0 deletions examples/pages-router/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
40 changes: 40 additions & 0 deletions examples/pages-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
20 changes: 20 additions & 0 deletions examples/pages-router/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
6 changes: 6 additions & 0 deletions examples/pages-router/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true
};

export default nextConfig;
Loading