@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/dist/CasProvider.d.ts +63 -0
  4. package/dist/CasProvider.d.ts.map +1 -0
  5. package/dist/CasProvider.js +150 -0
  6. package/dist/CasProvider.js.map +1 -0
  7. package/dist/components/CasLoginButton.d.ts +36 -0
  8. package/dist/components/CasLoginButton.d.ts.map +1 -0
  9. package/dist/components/CasLoginButton.js +23 -0
  10. package/dist/components/CasLoginButton.js.map +1 -0
  11. package/dist/components/CasProtectedRoute.d.ts +51 -0
  12. package/dist/components/CasProtectedRoute.d.ts.map +1 -0
  13. package/dist/components/CasProtectedRoute.js +58 -0
  14. package/dist/components/CasProtectedRoute.js.map +1 -0
  15. package/dist/handlers/callback.d.ts +39 -0
  16. package/dist/handlers/callback.d.ts.map +1 -0
  17. package/dist/handlers/callback.js +65 -0
  18. package/dist/handlers/callback.js.map +1 -0
  19. package/dist/handlers/index.d.ts +8 -0
  20. package/dist/handlers/index.d.ts.map +1 -0
  21. package/dist/handlers/index.js +8 -0
  22. package/dist/handlers/index.js.map +1 -0
  23. package/dist/handlers/logout.d.ts +36 -0
  24. package/dist/handlers/logout.d.ts.map +1 -0
  25. package/dist/handlers/logout.js +61 -0
  26. package/dist/handlers/logout.js.map +1 -0
  27. package/dist/handlers/user.d.ts +27 -0
  28. package/dist/handlers/user.d.ts.map +1 -0
  29. package/dist/handlers/user.js +44 -0
  30. package/dist/handlers/user.js.map +1 -0
  31. package/dist/hooks/useCasAuth.d.ts +32 -0
  32. package/dist/hooks/useCasAuth.d.ts.map +1 -0
  33. package/dist/hooks/useCasAuth.js +14 -0
  34. package/dist/hooks/useCasAuth.js.map +1 -0
  35. package/dist/hooks/useCasUser.d.ts +33 -0
  36. package/dist/hooks/useCasUser.d.ts.map +1 -0
  37. package/dist/hooks/useCasUser.js +45 -0
  38. package/dist/hooks/useCasUser.js.map +1 -0
  39. package/dist/index.d.ts +19 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +17 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/middleware.d.ts +40 -0
  44. package/dist/middleware.d.ts.map +1 -0
  45. package/dist/middleware.js +111 -0
  46. package/dist/middleware.js.map +1 -0
  47. package/dist/server/auth.d.ts +111 -0
  48. package/dist/server/auth.d.ts.map +1 -0
  49. package/dist/server/auth.js +215 -0
  50. package/dist/server/auth.js.map +1 -0
  51. package/dist/server/cas-client.d.ts +93 -0
  52. package/dist/server/cas-client.d.ts.map +1 -0
  53. package/dist/server/cas-client.js +222 -0
  54. package/dist/server/cas-client.js.map +1 -0
  55. package/dist/server/index.d.ts +12 -0
  56. package/dist/server/index.d.ts.map +1 -0
  57. package/dist/server/index.js +11 -0
  58. package/dist/server/index.js.map +1 -0
  59. package/dist/types.d.ts +170 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +6 -0
  62. package/dist/types.js.map +1 -0
  63. 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":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAc,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,GACT,MAAM,OAAO,CAAC;AAGf,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,UAAU,GAAG,aAAa,CAA6B,SAAS,CAAC,CAAC;AAsCxE,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,QAAQ,EACR,YAAY,GAAG,eAAe,EAC9B,cAAc,GAAG,iBAAiB,EAClC,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,WAAW,EACX,cAAc,GACG;IACjB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,0EAA0E;IAC1E,wBAAwB;IACxB,0EAA0E;IAE1E,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACvC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;gBACpC,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,UAAU;aAClB,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;gBACrD,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,QAAQ,CACN,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CACtE,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,SAAS,EAAE,CAAC;IACnB,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,0EAA0E;IAC1E,UAAU;IACV,0EAA0E;IAE1E,MAAM,KAAK,GAAG,WAAW,CACvB,CAAC,SAAkB,EAAE,EAAE;QACrB,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;YAClC,IAAI,SAAS;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC5D,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO;QACT,CAAC;QAED,IAAI,YAAY,IAAI,WAAW,EAAE,CAAC;YAChC,MAAM,QAAQ,GACZ,cAAc;gBACd,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,mBAAmB,CAAC;YAE/C,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7C,IAAI,SAAS;gBAAE,kBAAkB,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3E,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;gBACjC,SAAS,EAAE,WAAW;gBACtB,aAAa,EAAE,OAAO;gBACtB,YAAY,EAAE,kBAAkB,CAAC,QAAQ,EAAE;aAC5C,CAAC,CAAC;YACH,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC5F,OAAO;QACT,CAAC;QAED,4CAA4C;QAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAC5D,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxC,CAAC,EACD,CAAC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,CAAC,CAC1D,CAAC;IAEF,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,cAAc,EAAE;gBAC1B,MAAM,EAAE,MAAM;gBACd,WAAW,EAAE,aAAa;aAC3B,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC;IAC7B,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,0EAA0E;IAC1E,yBAAyB;IACzB,0EAA0E;IAE1E,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,IAAI;QACJ,SAAS;QACT,eAAe,EAAE,CAAC,CAAC,IAAI;QACvB,KAAK;QACL,KAAK;QACL,MAAM;QACN,OAAO;KACR,CAAC,EACF,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CACjD,CAAC;IAEF,OAAO,KAAC,UAAU,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAuB,CAAC;AAC7E,CAAC;AAED,8EAA8E;AAC9E,sCAAsC;AACtC,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,yDAAyD;YACvD,qDAAqD,CACxD,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -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"}