@cas-system/nextjs-cas-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/CasProvider.d.ts +63 -0
- package/dist/CasProvider.d.ts.map +1 -0
- package/dist/CasProvider.js +150 -0
- package/dist/CasProvider.js.map +1 -0
- package/dist/components/CasLoginButton.d.ts +36 -0
- package/dist/components/CasLoginButton.d.ts.map +1 -0
- package/dist/components/CasLoginButton.js +23 -0
- package/dist/components/CasLoginButton.js.map +1 -0
- package/dist/components/CasProtectedRoute.d.ts +51 -0
- package/dist/components/CasProtectedRoute.d.ts.map +1 -0
- package/dist/components/CasProtectedRoute.js +58 -0
- package/dist/components/CasProtectedRoute.js.map +1 -0
- package/dist/handlers/callback.d.ts +39 -0
- package/dist/handlers/callback.d.ts.map +1 -0
- package/dist/handlers/callback.js +65 -0
- package/dist/handlers/callback.js.map +1 -0
- package/dist/handlers/index.d.ts +8 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +8 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/logout.d.ts +36 -0
- package/dist/handlers/logout.d.ts.map +1 -0
- package/dist/handlers/logout.js +61 -0
- package/dist/handlers/logout.js.map +1 -0
- package/dist/handlers/user.d.ts +27 -0
- package/dist/handlers/user.d.ts.map +1 -0
- package/dist/handlers/user.js +44 -0
- package/dist/handlers/user.js.map +1 -0
- package/dist/hooks/useCasAuth.d.ts +32 -0
- package/dist/hooks/useCasAuth.d.ts.map +1 -0
- package/dist/hooks/useCasAuth.js +14 -0
- package/dist/hooks/useCasAuth.js.map +1 -0
- package/dist/hooks/useCasUser.d.ts +33 -0
- package/dist/hooks/useCasUser.d.ts.map +1 -0
- package/dist/hooks/useCasUser.js +45 -0
- package/dist/hooks/useCasUser.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +40 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +111 -0
- package/dist/middleware.js.map +1 -0
- package/dist/server/auth.d.ts +111 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +215 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/cas-client.d.ts +93 -0
- package/dist/server/cas-client.d.ts.map +1 -0
- package/dist/server/cas-client.js +222 -0
- package/dist/server/cas-client.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +11 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types.d.ts +170 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InSol-2021
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @cas-system/nextjs-cas-client
|
|
2
|
+
|
|
3
|
+
Next.js CAS (Central Authentication System) Client SDK for the **One System** CAS server.
|
|
4
|
+
Supports the App Router, Server Components, Route Handlers, Middleware, and client-side hooks.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @cas-system/nextjs-cas-client
|
|
10
|
+
# peer deps: next >= 14, react >= 18, react-dom >= 18
|
|
11
|
+
# runtime: Node.js >= 18.17 (App Router). Verified on Node 22.
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## How the SSO flow works
|
|
15
|
+
|
|
16
|
+
1. The browser is redirected to `GET {CAS_SERVER_URL}/sso/login?client_id={CLIENT_ID}`.
|
|
17
|
+
2. After authenticating, the CAS server 302-redirects to your registered callback URL
|
|
18
|
+
with the token appended: `{callbackUrl}?token={JWT}`.
|
|
19
|
+
3. Your callback route handler reads the `token` query parameter and validates it
|
|
20
|
+
**server-to-server** via `POST {CAS_SERVER_URL}/api/validate-token`
|
|
21
|
+
(body: `{ token, client_id, client_secret }`). The token is **single-use**.
|
|
22
|
+
4. On success the handler establishes an `HttpOnly`, signed session cookie.
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
Server-side configuration (`CasServerConfig`):
|
|
27
|
+
|
|
28
|
+
| Field | Env var (suggested) | Description |
|
|
29
|
+
| -------------- | --------------------- | -------------------------------------------- |
|
|
30
|
+
| `serverUrl` | `CAS_SERVER_URL` | Base URL of the CAS server (no trailing `/`) |
|
|
31
|
+
| `clientId` | `CAS_CLIENT_ID` | Registered client identifier |
|
|
32
|
+
| `clientSecret` | `CAS_CLIENT_SECRET` | Secret for server-to-server token validation |
|
|
33
|
+
| `callbackUrl` | `CAS_CALLBACK_URL` | Optional; defaults to `/api/cas/callback` |
|
|
34
|
+
|
|
35
|
+
The session cookie is signed with `CAS_COOKIE_SECRET` (falls back to `CAS_CLIENT_SECRET`).
|
|
36
|
+
|
|
37
|
+
## Route handlers (App Router)
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// app/api/cas/callback/route.ts
|
|
41
|
+
import { createCallbackHandler } from '@cas-system/nextjs-cas-client/handlers';
|
|
42
|
+
|
|
43
|
+
export const GET = createCallbackHandler({
|
|
44
|
+
cas: {
|
|
45
|
+
serverUrl: process.env.CAS_SERVER_URL!,
|
|
46
|
+
clientId: process.env.CAS_CLIENT_ID!,
|
|
47
|
+
clientSecret: process.env.CAS_CLIENT_SECRET!,
|
|
48
|
+
},
|
|
49
|
+
afterLoginUrl: '/dashboard',
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// app/api/cas/logout/route.ts
|
|
55
|
+
import { createLogoutHandler } from '@cas-system/nextjs-cas-client/handlers';
|
|
56
|
+
|
|
57
|
+
export const POST = createLogoutHandler({
|
|
58
|
+
cas: {
|
|
59
|
+
serverUrl: process.env.CAS_SERVER_URL!,
|
|
60
|
+
clientId: process.env.CAS_CLIENT_ID!,
|
|
61
|
+
clientSecret: process.env.CAS_CLIENT_SECRET!,
|
|
62
|
+
},
|
|
63
|
+
afterLogoutUrl: '/',
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// app/api/cas/user/route.ts
|
|
69
|
+
import { createUserHandler } from '@cas-system/nextjs-cas-client/handlers';
|
|
70
|
+
|
|
71
|
+
export const GET = createUserHandler();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Middleware
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// middleware.ts
|
|
78
|
+
import { createCasMiddleware } from '@cas-system/nextjs-cas-client/middleware';
|
|
79
|
+
|
|
80
|
+
export default createCasMiddleware({
|
|
81
|
+
protectedPaths: ['/dashboard', '/admin'],
|
|
82
|
+
publicPaths: ['/api/health'],
|
|
83
|
+
loginPath: '/login', // optional
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const config = {
|
|
87
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The middleware reads `CAS_SERVER_URL`, `CAS_CLIENT_ID`, and `CAS_CALLBACK_URL`
|
|
92
|
+
from the environment when `loginPath` is not configured.
|
|
93
|
+
|
|
94
|
+
## Client provider + hooks
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// app/layout.tsx
|
|
98
|
+
import { CasProvider } from '@cas-system/nextjs-cas-client';
|
|
99
|
+
|
|
100
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
101
|
+
return (
|
|
102
|
+
<html>
|
|
103
|
+
<body>
|
|
104
|
+
{/* casServerUrl / casClientId / casCallbackUrl let login() build the
|
|
105
|
+
CAS SSO redirect. They are NEXT_PUBLIC_* (no secret). */}
|
|
106
|
+
<CasProvider
|
|
107
|
+
casServerUrl={process.env.NEXT_PUBLIC_CAS_SERVER_URL}
|
|
108
|
+
casClientId={process.env.NEXT_PUBLIC_CAS_CLIENT_ID}
|
|
109
|
+
casCallbackUrl={process.env.NEXT_PUBLIC_CAS_CALLBACK_URL}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
</CasProvider>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
'use client';
|
|
121
|
+
import { useCasAuth, useCasUser, CasLoginButton, CasProtectedRoute } from '@cas-system/nextjs-cas-client';
|
|
122
|
+
|
|
123
|
+
export function Navbar() {
|
|
124
|
+
const { user, isAuthenticated, login, logout } = useCasAuth();
|
|
125
|
+
return isAuthenticated
|
|
126
|
+
? <button onClick={logout}>Sign out ({user?.username})</button>
|
|
127
|
+
: <CasLoginButton>Sign in with SSO</CasLoginButton>;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Server-side session helpers
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { cookies } from 'next/headers';
|
|
135
|
+
import { getCasSession, withCasAuth, CasClient } from '@cas-system/nextjs-cas-client/server';
|
|
136
|
+
|
|
137
|
+
// In a Server Component / Route Handler
|
|
138
|
+
const session = await getCasSession(await cookies());
|
|
139
|
+
|
|
140
|
+
// Protect a route handler
|
|
141
|
+
export const GET = withCasAuth(async (req, ctx, user) => {
|
|
142
|
+
return Response.json({ message: `Hello ${user.username}` });
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @cas-system/nextjs-cas-client/CasProvider
|
|
3
|
+
* @description Client-side React context provider that manages CAS
|
|
4
|
+
* authentication state. Fetches the current session from
|
|
5
|
+
* `/api/cas/user` on mount and exposes login / logout / refresh actions.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/layout.tsx
|
|
10
|
+
* import { CasProvider } from '@cas-system/nextjs-cas-client';
|
|
11
|
+
*
|
|
12
|
+
* export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
13
|
+
* return (
|
|
14
|
+
* <html>
|
|
15
|
+
* <body>
|
|
16
|
+
* <CasProvider>{children}</CasProvider>
|
|
17
|
+
* </body>
|
|
18
|
+
* </html>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import type { CasAuthContext } from './types';
|
|
25
|
+
/** Props accepted by {@link CasProvider}. */
|
|
26
|
+
export interface CasProviderProps {
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Custom endpoint that returns the current user JSON.
|
|
30
|
+
* @default '/api/cas/user'
|
|
31
|
+
*/
|
|
32
|
+
userEndpoint?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Custom endpoint for logout.
|
|
35
|
+
* @default '/api/cas/logout'
|
|
36
|
+
*/
|
|
37
|
+
logoutEndpoint?: string;
|
|
38
|
+
/**
|
|
39
|
+
* CAS login URL (client-side redirect).
|
|
40
|
+
* When omitted the provider builds it from `casServerUrl` and `casClientId`.
|
|
41
|
+
*/
|
|
42
|
+
loginUrl?: string;
|
|
43
|
+
/** CAS server URL — used to build the login redirect when `loginUrl` is not provided. */
|
|
44
|
+
casServerUrl?: string;
|
|
45
|
+
/** CAS client ID — used to build the login redirect when `loginUrl` is not provided. */
|
|
46
|
+
casClientId?: string;
|
|
47
|
+
/** Callback URL — used to build the login redirect when `loginUrl` is not provided. */
|
|
48
|
+
casCallbackUrl?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Client-side authentication provider.
|
|
52
|
+
*
|
|
53
|
+
* Wrap your application (typically in `app/layout.tsx`) with this provider to
|
|
54
|
+
* make CAS auth state available to all client components via the
|
|
55
|
+
* {@link useCasAuth} and {@link useCasUser} hooks.
|
|
56
|
+
*/
|
|
57
|
+
export declare function CasProvider({ children, userEndpoint, logoutEndpoint, loginUrl: loginUrlProp, casServerUrl, casClientId, casCallbackUrl, }: CasProviderProps): React.JSX.Element;
|
|
58
|
+
/**
|
|
59
|
+
* Access the CAS auth context. Must be called inside a `<CasProvider>`.
|
|
60
|
+
* @throws If used outside of a `CasProvider`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function useCasAuthContext(): CasAuthContext;
|
|
63
|
+
//# sourceMappingURL=CasProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasProvider.d.ts","sourceRoot":"","sources":["../src/CasProvider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,SAAS,CAAC;AAYvD,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,YAA8B,EAC9B,cAAkC,EAClC,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,WAAW,EACX,cAAc,GACf,EAAE,gBAAgB,qBA8GlB;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CASlD"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* @module @cas-system/nextjs-cas-client/CasProvider
|
|
5
|
+
* @description Client-side React context provider that manages CAS
|
|
6
|
+
* authentication state. Fetches the current session from
|
|
7
|
+
* `/api/cas/user` on mount and exposes login / logout / refresh actions.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* // app/layout.tsx
|
|
12
|
+
* import { CasProvider } from '@cas-system/nextjs-cas-client';
|
|
13
|
+
*
|
|
14
|
+
* export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
15
|
+
* return (
|
|
16
|
+
* <html>
|
|
17
|
+
* <body>
|
|
18
|
+
* <CasProvider>{children}</CasProvider>
|
|
19
|
+
* </body>
|
|
20
|
+
* </html>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Context
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const CasContext = createContext(undefined);
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Provider component
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Client-side authentication provider.
|
|
35
|
+
*
|
|
36
|
+
* Wrap your application (typically in `app/layout.tsx`) with this provider to
|
|
37
|
+
* make CAS auth state available to all client components via the
|
|
38
|
+
* {@link useCasAuth} and {@link useCasUser} hooks.
|
|
39
|
+
*/
|
|
40
|
+
export function CasProvider({ children, userEndpoint = '/api/cas/user', logoutEndpoint = '/api/cas/logout', loginUrl: loginUrlProp, casServerUrl, casClientId, casCallbackUrl, }) {
|
|
41
|
+
const [user, setUser] = useState(null);
|
|
42
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
// -----------------------------------------------------------------------
|
|
45
|
+
// Fetch current session
|
|
46
|
+
// -----------------------------------------------------------------------
|
|
47
|
+
const fetchUser = useCallback(async () => {
|
|
48
|
+
setIsLoading(true);
|
|
49
|
+
setError(null);
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetch(userEndpoint, {
|
|
52
|
+
credentials: 'same-origin',
|
|
53
|
+
cache: 'no-store',
|
|
54
|
+
});
|
|
55
|
+
if (res.ok) {
|
|
56
|
+
const data = (await res.json());
|
|
57
|
+
setUser(data.user ?? null);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
setUser(null);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
setUser(null);
|
|
65
|
+
setError(err instanceof Error ? err : new Error('Failed to fetch CAS session'));
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
}
|
|
70
|
+
}, [userEndpoint]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
void fetchUser();
|
|
73
|
+
}, [fetchUser]);
|
|
74
|
+
// -----------------------------------------------------------------------
|
|
75
|
+
// Actions
|
|
76
|
+
// -----------------------------------------------------------------------
|
|
77
|
+
const login = useCallback((returnUrl) => {
|
|
78
|
+
if (loginUrlProp) {
|
|
79
|
+
const url = new URL(loginUrlProp);
|
|
80
|
+
if (returnUrl)
|
|
81
|
+
url.searchParams.set('returnUrl', returnUrl);
|
|
82
|
+
window.location.href = url.toString();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (casServerUrl && casClientId) {
|
|
86
|
+
const callback = casCallbackUrl ??
|
|
87
|
+
`${window.location.origin}/api/cas/callback`;
|
|
88
|
+
const callbackWithReturn = new URL(callback);
|
|
89
|
+
if (returnUrl)
|
|
90
|
+
callbackWithReturn.searchParams.set('returnUrl', returnUrl);
|
|
91
|
+
const params = new URLSearchParams({
|
|
92
|
+
client_id: casClientId,
|
|
93
|
+
response_type: 'token',
|
|
94
|
+
redirect_uri: callbackWithReturn.toString(),
|
|
95
|
+
});
|
|
96
|
+
window.location.href = `${casServerUrl.replace(/\/+$/, '')}/sso/login?${params.toString()}`;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Fallback — redirect to a local login page
|
|
100
|
+
const url = new URL('/login', window.location.origin);
|
|
101
|
+
if (returnUrl)
|
|
102
|
+
url.searchParams.set('returnUrl', returnUrl);
|
|
103
|
+
window.location.href = url.toString();
|
|
104
|
+
}, [loginUrlProp, casServerUrl, casClientId, casCallbackUrl]);
|
|
105
|
+
const logout = useCallback(async () => {
|
|
106
|
+
try {
|
|
107
|
+
await fetch(logoutEndpoint, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
credentials: 'same-origin',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Ignore — cookie might already be gone
|
|
114
|
+
}
|
|
115
|
+
setUser(null);
|
|
116
|
+
window.location.href = '/';
|
|
117
|
+
}, [logoutEndpoint]);
|
|
118
|
+
const refresh = useCallback(async () => {
|
|
119
|
+
await fetchUser();
|
|
120
|
+
}, [fetchUser]);
|
|
121
|
+
// -----------------------------------------------------------------------
|
|
122
|
+
// Memoised context value
|
|
123
|
+
// -----------------------------------------------------------------------
|
|
124
|
+
const value = useMemo(() => ({
|
|
125
|
+
user,
|
|
126
|
+
isLoading,
|
|
127
|
+
isAuthenticated: !!user,
|
|
128
|
+
error,
|
|
129
|
+
login,
|
|
130
|
+
logout,
|
|
131
|
+
refresh,
|
|
132
|
+
}), [user, isLoading, error, login, logout, refresh]);
|
|
133
|
+
return _jsx(CasContext.Provider, { value: value, children: children });
|
|
134
|
+
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Hook (also re-exported from hooks/)
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
/**
|
|
139
|
+
* Access the CAS auth context. Must be called inside a `<CasProvider>`.
|
|
140
|
+
* @throws If used outside of a `CasProvider`.
|
|
141
|
+
*/
|
|
142
|
+
export function useCasAuthContext() {
|
|
143
|
+
const ctx = useContext(CasContext);
|
|
144
|
+
if (!ctx) {
|
|
145
|
+
throw new Error('useCasAuthContext must be used within a <CasProvider>. ' +
|
|
146
|
+
'Wrap your app in <CasProvider> in your root layout.');
|
|
147
|
+
}
|
|
148
|
+
return ctx;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=CasProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasProvider.js","sourceRoot":"","sources":["../src/CasProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @cas-system/nextjs-cas-client/components/CasLoginButton
|
|
3
|
+
* @description A ready-to-use login button that redirects to the CAS SSO
|
|
4
|
+
* login page when clicked.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { CasLoginButton } from '@cas-system/nextjs-cas-client';
|
|
9
|
+
*
|
|
10
|
+
* export default function Header() {
|
|
11
|
+
* return (
|
|
12
|
+
* <nav>
|
|
13
|
+
* <CasLoginButton>Sign in with SSO</CasLoginButton>
|
|
14
|
+
* </nav>
|
|
15
|
+
* );
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import React from 'react';
|
|
20
|
+
/** Props for {@link CasLoginButton}. */
|
|
21
|
+
export interface CasLoginButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> {
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* URL to return to after login.
|
|
25
|
+
* Defaults to the current page.
|
|
26
|
+
*/
|
|
27
|
+
returnUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A simple button that triggers the CAS login flow.
|
|
31
|
+
*
|
|
32
|
+
* When the user is already authenticated the button renders nothing by
|
|
33
|
+
* default. Customise this via the {@link CasLoginButtonProps}.
|
|
34
|
+
*/
|
|
35
|
+
export declare function CasLoginButton({ children, returnUrl, ...buttonProps }: CasLoginButtonProps): React.JSX.Element | null;
|
|
36
|
+
//# sourceMappingURL=CasLoginButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasLoginButton.d.ts","sourceRoot":"","sources":["../../src/components/CasLoginButton.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,wCAAwC;AACxC,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IACtE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAoB,EACpB,SAAS,EACT,GAAG,WAAW,EACf,EAAE,mBAAmB,4BA8BrB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCasAuthContext } from '../CasProvider';
|
|
4
|
+
/**
|
|
5
|
+
* A simple button that triggers the CAS login flow.
|
|
6
|
+
*
|
|
7
|
+
* When the user is already authenticated the button renders nothing by
|
|
8
|
+
* default. Customise this via the {@link CasLoginButtonProps}.
|
|
9
|
+
*/
|
|
10
|
+
export function CasLoginButton({ children = 'Sign in', returnUrl, ...buttonProps }) {
|
|
11
|
+
const { isAuthenticated, isLoading, login } = useCasAuthContext();
|
|
12
|
+
if (isLoading) {
|
|
13
|
+
return (_jsx("button", { ...buttonProps, disabled: true, children: "Loading\u2026" }));
|
|
14
|
+
}
|
|
15
|
+
if (isAuthenticated) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return (_jsx("button", { ...buttonProps, onClick: () => login(returnUrl ??
|
|
19
|
+
(typeof window !== 'undefined'
|
|
20
|
+
? window.location.pathname + window.location.search
|
|
21
|
+
: undefined)), children: children }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=CasLoginButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasLoginButton.js","sourceRoot":"","sources":["../../src/components/CasLoginButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAsBb,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAcnD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,GAAG,SAAS,EACpB,SAAS,EACT,GAAG,WAAW,EACM;IACpB,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAElE,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,oBAAY,WAAW,EAAE,QAAQ,oCAExB,CACV,CAAC;IACJ,CAAC;IAED,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,oBACM,WAAW,EACf,OAAO,EAAE,GAAG,EAAE,CACZ,KAAK,CACH,SAAS;YACP,CAAC,OAAO,MAAM,KAAK,WAAW;gBAC5B,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM;gBACnD,CAAC,CAAC,SAAS,CAAC,CACjB,YAGF,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @cas-system/nextjs-cas-client/components/CasProtectedRoute
|
|
3
|
+
* @description Client component that guards its children behind CAS
|
|
4
|
+
* authentication. Shows a loading state while the session is being
|
|
5
|
+
* fetched and redirects to login when no valid session exists.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { CasProtectedRoute } from '@cas-system/nextjs-cas-client';
|
|
10
|
+
*
|
|
11
|
+
* export default function SecretPage() {
|
|
12
|
+
* return (
|
|
13
|
+
* <CasProtectedRoute>
|
|
14
|
+
* <h1>Secret Content</h1>
|
|
15
|
+
* </CasProtectedRoute>
|
|
16
|
+
* );
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import React from 'react';
|
|
21
|
+
/** Props for {@link CasProtectedRoute}. */
|
|
22
|
+
export interface CasProtectedRouteProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Optional list of required roles. The user must possess **all** of
|
|
26
|
+
* these roles to see the content.
|
|
27
|
+
*/
|
|
28
|
+
roles?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Custom loading element shown while the session is being fetched.
|
|
31
|
+
* @default A simple "Loading…" paragraph.
|
|
32
|
+
*/
|
|
33
|
+
loadingFallback?: React.ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Custom element shown when the user is authenticated but lacks the
|
|
36
|
+
* required roles.
|
|
37
|
+
* @default A simple "Access denied" paragraph.
|
|
38
|
+
*/
|
|
39
|
+
unauthorizedFallback?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* URL to redirect to when not authenticated.
|
|
42
|
+
* If omitted the provider's `login()` action is invoked instead.
|
|
43
|
+
*/
|
|
44
|
+
redirectTo?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Client component that only renders its children when the user is
|
|
48
|
+
* authenticated (and optionally has the required roles).
|
|
49
|
+
*/
|
|
50
|
+
export declare function CasProtectedRoute({ children, roles, loadingFallback, unauthorizedFallback, redirectTo, }: CasProtectedRouteProps): React.JSX.Element;
|
|
51
|
+
//# sourceMappingURL=CasProtectedRoute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasProtectedRoute.d.ts","sourceRoot":"","sources":["../../src/components/CasProtectedRoute.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAoB,MAAM,OAAO,CAAC;AAGzC,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;;OAGG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAElC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEvC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,KAAK,EACL,eAAe,EACf,oBAAoB,EACpB,UAAU,GACX,EAAE,sBAAsB,qBAmCxB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* @module @cas-system/nextjs-cas-client/components/CasProtectedRoute
|
|
5
|
+
* @description Client component that guards its children behind CAS
|
|
6
|
+
* authentication. Shows a loading state while the session is being
|
|
7
|
+
* fetched and redirects to login when no valid session exists.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { CasProtectedRoute } from '@cas-system/nextjs-cas-client';
|
|
12
|
+
*
|
|
13
|
+
* export default function SecretPage() {
|
|
14
|
+
* return (
|
|
15
|
+
* <CasProtectedRoute>
|
|
16
|
+
* <h1>Secret Content</h1>
|
|
17
|
+
* </CasProtectedRoute>
|
|
18
|
+
* );
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import { useEffect } from 'react';
|
|
23
|
+
import { useCasAuthContext } from '../CasProvider';
|
|
24
|
+
/**
|
|
25
|
+
* Client component that only renders its children when the user is
|
|
26
|
+
* authenticated (and optionally has the required roles).
|
|
27
|
+
*/
|
|
28
|
+
export function CasProtectedRoute({ children, roles, loadingFallback, unauthorizedFallback, redirectTo, }) {
|
|
29
|
+
const { user, isLoading, isAuthenticated, login } = useCasAuthContext();
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!isLoading && !isAuthenticated) {
|
|
32
|
+
if (redirectTo) {
|
|
33
|
+
window.location.href = redirectTo;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
login(window.location.pathname + window.location.search);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, [isLoading, isAuthenticated, login, redirectTo]);
|
|
40
|
+
// Still loading
|
|
41
|
+
if (isLoading) {
|
|
42
|
+
return _jsx(_Fragment, { children: loadingFallback ?? _jsx("p", { children: "Loading\u2026" }) });
|
|
43
|
+
}
|
|
44
|
+
// Not authenticated — will redirect in useEffect
|
|
45
|
+
if (!isAuthenticated) {
|
|
46
|
+
return _jsx(_Fragment, { children: loadingFallback ?? _jsx("p", { children: "Redirecting to login\u2026" }) });
|
|
47
|
+
}
|
|
48
|
+
// Role check
|
|
49
|
+
if (roles && roles.length > 0) {
|
|
50
|
+
const userRoles = user?.roles ?? [];
|
|
51
|
+
const hasRequiredRoles = roles.every((r) => userRoles.includes(r));
|
|
52
|
+
if (!hasRequiredRoles) {
|
|
53
|
+
return (_jsx(_Fragment, { children: unauthorizedFallback ?? _jsx("p", { children: "Access denied \u2014 insufficient permissions." }) }));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return _jsx(_Fragment, { children: children });
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=CasProtectedRoute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CasProtectedRoute.js","sourceRoot":"","sources":["../../src/components/CasProtectedRoute.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAgCnD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,QAAQ,EACR,KAAK,EACL,eAAe,EACf,oBAAoB,EACpB,UAAU,GACa;IACvB,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAExE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS,IAAI,CAAC,eAAe,EAAE,CAAC;YACnC,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,UAAU,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IAEpD,gBAAgB;IAChB,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,4BAAG,eAAe,IAAI,wCAAe,GAAI,CAAC;IACnD,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,4BAAG,eAAe,IAAI,qDAA4B,GAAI,CAAC;IAChE,CAAC;IAED,aAAa;IACb,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,CACL,4BAAG,oBAAoB,IAAI,yEAAgD,GAAI,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @cas-system/nextjs-cas-client/handlers/callback
|
|
3
|
+
* @description API route handler for the CAS SSO callback endpoint.
|
|
4
|
+
*
|
|
5
|
+
* After the CAS server authenticates the user it redirects to
|
|
6
|
+
* `{callbackUrl}?token=JWT_TOKEN`. This handler:
|
|
7
|
+
*
|
|
8
|
+
* 1. Extracts the `token` query parameter.
|
|
9
|
+
* 2. Validates it server-side via the CAS `/api/validate-token` endpoint.
|
|
10
|
+
* 3. Sets an encrypted session cookie.
|
|
11
|
+
* 4. Redirects the user to the intended URL (or `/`).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // app/api/cas/callback/route.ts
|
|
16
|
+
* import { createCallbackHandler } from '@cas-system/nextjs-cas-client/handlers';
|
|
17
|
+
*
|
|
18
|
+
* const handler = createCallbackHandler({
|
|
19
|
+
* cas: {
|
|
20
|
+
* serverUrl: process.env.CAS_SERVER_URL!,
|
|
21
|
+
* clientId: process.env.CAS_CLIENT_ID!,
|
|
22
|
+
* clientSecret: process.env.CAS_CLIENT_SECRET!,
|
|
23
|
+
* },
|
|
24
|
+
* afterLoginUrl: '/dashboard',
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* export { handler as GET };
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
31
|
+
import type { CasHandlerConfig } from '../types';
|
|
32
|
+
/**
|
|
33
|
+
* Create a GET handler for `/api/cas/callback`.
|
|
34
|
+
*
|
|
35
|
+
* @param config - Handler configuration including CAS server details.
|
|
36
|
+
* @returns A Next.js App Router GET handler.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createCallbackHandler(config: CasHandlerConfig): (request: NextRequest) => Promise<NextResponse>;
|
|
39
|
+
//# sourceMappingURL=callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/handlers/callback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIjD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,IAIlC,SAAS,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC,CA4BvE"}
|