@cas-system/nextjs-cas-client 1.0.0 → 1.0.1
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/dist/{CasProvider.d.ts → cjs/CasProvider.d.ts} +1 -1
- package/dist/cjs/CasProvider.d.ts.map +1 -0
- package/dist/cjs/CasProvider.js +154 -0
- package/dist/cjs/CasProvider.js.map +1 -0
- package/dist/cjs/components/CasLoginButton.d.ts.map +1 -0
- package/dist/cjs/components/CasLoginButton.js +26 -0
- package/dist/cjs/components/CasLoginButton.js.map +1 -0
- package/dist/cjs/components/CasProtectedRoute.d.ts.map +1 -0
- package/dist/cjs/components/CasProtectedRoute.js +61 -0
- package/dist/cjs/components/CasProtectedRoute.js.map +1 -0
- package/dist/{handlers → cjs/handlers}/callback.d.ts +2 -2
- package/dist/cjs/handlers/callback.d.ts.map +1 -0
- package/dist/cjs/handlers/callback.js +68 -0
- package/dist/cjs/handlers/callback.js.map +1 -0
- package/dist/cjs/handlers/index.d.ts +8 -0
- package/dist/cjs/handlers/index.d.ts.map +1 -0
- package/dist/cjs/handlers/index.js +14 -0
- package/dist/cjs/handlers/index.js.map +1 -0
- package/dist/{handlers → cjs/handlers}/logout.d.ts +2 -2
- package/dist/cjs/handlers/logout.d.ts.map +1 -0
- package/dist/cjs/handlers/logout.js +64 -0
- package/dist/cjs/handlers/logout.js.map +1 -0
- package/dist/{handlers → cjs/handlers}/user.d.ts +1 -1
- package/dist/cjs/handlers/user.d.ts.map +1 -0
- package/dist/cjs/handlers/user.js +47 -0
- package/dist/cjs/handlers/user.js.map +1 -0
- package/dist/{hooks → cjs/hooks}/useCasAuth.d.ts +1 -1
- package/dist/cjs/hooks/useCasAuth.d.ts.map +1 -0
- package/dist/cjs/hooks/useCasAuth.js +17 -0
- package/dist/cjs/hooks/useCasAuth.js.map +1 -0
- package/dist/{hooks → cjs/hooks}/useCasUser.d.ts +1 -1
- package/dist/cjs/hooks/useCasUser.d.ts.map +1 -0
- package/dist/cjs/hooks/useCasUser.js +48 -0
- package/dist/cjs/hooks/useCasUser.js.map +1 -0
- package/dist/{index.d.ts → cjs/index.d.ts} +10 -10
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +26 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/{middleware.d.ts → cjs/middleware.d.ts} +2 -2
- package/dist/cjs/middleware.d.ts.map +1 -0
- package/dist/cjs/middleware.js +114 -0
- package/dist/cjs/middleware.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/{server → cjs/server}/auth.d.ts +2 -2
- package/dist/cjs/server/auth.d.ts.map +1 -0
- package/dist/cjs/server/auth.js +222 -0
- package/dist/cjs/server/auth.js.map +1 -0
- package/dist/{server → cjs/server}/cas-client.d.ts +1 -1
- package/dist/cjs/server/cas-client.d.ts.map +1 -0
- package/dist/cjs/server/cas-client.js +226 -0
- package/dist/cjs/server/cas-client.js.map +1 -0
- package/dist/{server → cjs/server}/index.d.ts +3 -3
- package/dist/cjs/server/index.d.ts.map +1 -0
- package/dist/cjs/server/index.js +20 -0
- package/dist/cjs/server/index.js.map +1 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +7 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/CasProvider.d.ts +63 -0
- package/dist/esm/CasProvider.d.ts.map +1 -0
- package/dist/esm/CasProvider.js.map +1 -0
- package/dist/esm/components/CasLoginButton.d.ts +36 -0
- package/dist/esm/components/CasLoginButton.d.ts.map +1 -0
- package/dist/{components → esm/components}/CasLoginButton.js +1 -1
- package/dist/esm/components/CasLoginButton.js.map +1 -0
- package/dist/esm/components/CasProtectedRoute.d.ts +51 -0
- package/dist/esm/components/CasProtectedRoute.d.ts.map +1 -0
- package/dist/{components → esm/components}/CasProtectedRoute.js +1 -1
- package/dist/esm/components/CasProtectedRoute.js.map +1 -0
- package/dist/esm/handlers/callback.d.ts +39 -0
- package/dist/esm/handlers/callback.d.ts.map +1 -0
- package/dist/{handlers → esm/handlers}/callback.js +4 -4
- package/dist/esm/handlers/callback.js.map +1 -0
- package/dist/esm/handlers/index.d.ts +8 -0
- package/dist/esm/handlers/index.d.ts.map +1 -0
- package/dist/esm/handlers/index.js +8 -0
- package/dist/esm/handlers/index.js.map +1 -0
- package/dist/esm/handlers/logout.d.ts +36 -0
- package/dist/esm/handlers/logout.d.ts.map +1 -0
- package/dist/{handlers → esm/handlers}/logout.js +4 -4
- package/dist/esm/handlers/logout.js.map +1 -0
- package/dist/esm/handlers/user.d.ts +27 -0
- package/dist/esm/handlers/user.d.ts.map +1 -0
- package/dist/{handlers → esm/handlers}/user.js +3 -3
- package/dist/esm/handlers/user.js.map +1 -0
- package/dist/esm/hooks/useCasAuth.d.ts +32 -0
- package/dist/esm/hooks/useCasAuth.d.ts.map +1 -0
- package/dist/{hooks → esm/hooks}/useCasAuth.js +1 -1
- package/dist/esm/hooks/useCasAuth.js.map +1 -0
- package/dist/esm/hooks/useCasUser.d.ts +33 -0
- package/dist/esm/hooks/useCasUser.d.ts.map +1 -0
- package/dist/{hooks → esm/hooks}/useCasUser.js +1 -1
- package/dist/esm/hooks/useCasUser.js.map +1 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/{index.js → esm/index.js} +5 -5
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/middleware.d.ts +40 -0
- package/dist/esm/middleware.d.ts.map +1 -0
- package/dist/{middleware.js → esm/middleware.js} +2 -2
- package/dist/esm/middleware.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/server/auth.d.ts +111 -0
- package/dist/esm/server/auth.d.ts.map +1 -0
- package/dist/{server → esm/server}/auth.js +1 -1
- package/dist/esm/server/auth.js.map +1 -0
- package/dist/esm/server/cas-client.d.ts +93 -0
- package/dist/esm/server/cas-client.d.ts.map +1 -0
- package/dist/esm/server/cas-client.js.map +1 -0
- package/dist/esm/server/index.d.ts +12 -0
- package/dist/esm/server/index.d.ts.map +1 -0
- package/dist/{server → esm/server}/index.js +2 -2
- package/dist/esm/server/index.js.map +1 -0
- package/dist/esm/types.d.ts +170 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +39 -19
- package/dist/CasProvider.d.ts.map +0 -1
- package/dist/CasProvider.js.map +0 -1
- package/dist/components/CasLoginButton.d.ts.map +0 -1
- package/dist/components/CasLoginButton.js.map +0 -1
- package/dist/components/CasProtectedRoute.d.ts.map +0 -1
- package/dist/components/CasProtectedRoute.js.map +0 -1
- package/dist/handlers/callback.d.ts.map +0 -1
- package/dist/handlers/callback.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -8
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -8
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/logout.d.ts.map +0 -1
- package/dist/handlers/logout.js.map +0 -1
- package/dist/handlers/user.d.ts.map +0 -1
- package/dist/handlers/user.js.map +0 -1
- package/dist/hooks/useCasAuth.d.ts.map +0 -1
- package/dist/hooks/useCasAuth.js.map +0 -1
- package/dist/hooks/useCasUser.d.ts.map +0 -1
- package/dist/hooks/useCasUser.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js.map +0 -1
- package/dist/server/auth.d.ts.map +0 -1
- package/dist/server/auth.js.map +0 -1
- package/dist/server/cas-client.d.ts.map +0 -1
- package/dist/server/cas-client.js.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- /package/dist/{components → cjs/components}/CasLoginButton.d.ts +0 -0
- /package/dist/{components → cjs/components}/CasProtectedRoute.d.ts +0 -0
- /package/dist/{types.d.ts → cjs/types.d.ts} +0 -0
- /package/dist/{CasProvider.js → esm/CasProvider.js} +0 -0
- /package/dist/{server → esm/server}/cas-client.js +0 -0
- /package/dist/{types.js → esm/types.js} +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.useCasUser = useCasUser;
|
|
5
|
+
/**
|
|
6
|
+
* @module @cas-system/nextjs-cas-client/hooks/useCasUser
|
|
7
|
+
* @description Client-side hook focused on the current CAS user and
|
|
8
|
+
* role-based access control helpers.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* 'use client';
|
|
13
|
+
* import { useCasUser } from '@cas-system/nextjs-cas-client';
|
|
14
|
+
*
|
|
15
|
+
* export function AdminPanel() {
|
|
16
|
+
* const { user, hasRole, hasAnyRole } = useCasUser();
|
|
17
|
+
*
|
|
18
|
+
* if (!hasRole('admin')) {
|
|
19
|
+
* return <p>Access denied</p>;
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* return <h1>Welcome admin {user?.username}</h1>;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
const react_1 = require("react");
|
|
27
|
+
const CasProvider_js_1 = require("../CasProvider.js");
|
|
28
|
+
/**
|
|
29
|
+
* Hook for accessing the current CAS user and performing role checks.
|
|
30
|
+
*
|
|
31
|
+
* Must be used inside a `<CasProvider>`.
|
|
32
|
+
*/
|
|
33
|
+
function useCasUser() {
|
|
34
|
+
const { user, isLoading, isAuthenticated } = (0, CasProvider_js_1.useCasAuthContext)();
|
|
35
|
+
const roles = (0, react_1.useMemo)(() => user?.roles ?? [], [user]);
|
|
36
|
+
const hasRole = (0, react_1.useCallback)((...required) => {
|
|
37
|
+
if (!user?.roles)
|
|
38
|
+
return required.length === 0;
|
|
39
|
+
return required.every((r) => user.roles.includes(r));
|
|
40
|
+
}, [user]);
|
|
41
|
+
const hasAnyRole = (0, react_1.useCallback)((...candidates) => {
|
|
42
|
+
if (!user?.roles)
|
|
43
|
+
return false;
|
|
44
|
+
return candidates.some((r) => user.roles.includes(r));
|
|
45
|
+
}, [user]);
|
|
46
|
+
return { user, isLoading, isAuthenticated, hasRole, hasAnyRole, roles };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=useCasUser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCasUser.js","sourceRoot":"","sources":["../../../src/hooks/useCasUser.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAgEb,gCAsBC;AApFD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,iCAA6C;AAE7C,sDAAsD;AAiCtD;;;;GAIG;AACH,SAAgB,UAAU;IACxB,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,IAAA,kCAAiB,GAAE,CAAC;IAEjE,MAAM,KAAK,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,IAAA,mBAAW,EACzB,CAAC,GAAG,QAAkB,EAAE,EAAE;QACxB,IAAI,CAAC,IAAI,EAAE,KAAK;YAAE,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;QAC/C,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,MAAM,UAAU,GAAG,IAAA,mBAAW,EAC5B,CAAC,GAAG,UAAoB,EAAE,EAAE;QAC1B,IAAI,CAAC,IAAI,EAAE,KAAK;YAAE,OAAO,KAAK,CAAC;QAC/B,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAC1E,CAAC"}
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* Server-only helpers live under `@cas-system/nextjs-cas-client/server`,
|
|
7
7
|
* route handlers under `/handlers`, and middleware under `/middleware`.
|
|
8
8
|
*/
|
|
9
|
-
export { CasProvider, useCasAuthContext } from './CasProvider';
|
|
10
|
-
export type { CasProviderProps } from './CasProvider';
|
|
11
|
-
export { useCasAuth } from './hooks/useCasAuth';
|
|
12
|
-
export { useCasUser } from './hooks/useCasUser';
|
|
13
|
-
export type { UseCasUserReturn } from './hooks/useCasUser';
|
|
14
|
-
export { CasLoginButton } from './components/CasLoginButton';
|
|
15
|
-
export type { CasLoginButtonProps } from './components/CasLoginButton';
|
|
16
|
-
export { CasProtectedRoute } from './components/CasProtectedRoute';
|
|
17
|
-
export type { CasProtectedRouteProps } from './components/CasProtectedRoute';
|
|
18
|
-
export type { CasConfig, CasServerConfig, CasUser, CasSessionData, CasMiddlewareConfig, CasAuthContext, CasHandlerConfig, } from './types';
|
|
9
|
+
export { CasProvider, useCasAuthContext } from './CasProvider.js';
|
|
10
|
+
export type { CasProviderProps } from './CasProvider.js';
|
|
11
|
+
export { useCasAuth } from './hooks/useCasAuth.js';
|
|
12
|
+
export { useCasUser } from './hooks/useCasUser.js';
|
|
13
|
+
export type { UseCasUserReturn } from './hooks/useCasUser.js';
|
|
14
|
+
export { CasLoginButton } from './components/CasLoginButton.js';
|
|
15
|
+
export type { CasLoginButtonProps } from './components/CasLoginButton.js';
|
|
16
|
+
export { CasProtectedRoute } from './components/CasProtectedRoute.js';
|
|
17
|
+
export type { CasProtectedRouteProps } from './components/CasProtectedRoute.js';
|
|
18
|
+
export type { CasConfig, CasServerConfig, CasUser, CasSessionData, CasMiddlewareConfig, CasAuthContext, CasHandlerConfig, } from './types.js';
|
|
19
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,YAAY,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAGhF,YAAY,EACV,SAAS,EACT,eAAe,EACf,OAAO,EACP,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @cas-system/nextjs-cas-client
|
|
4
|
+
* @description Public entry point for the Next.js CAS Client SDK.
|
|
5
|
+
*
|
|
6
|
+
* Re-exports the client-side provider, hooks, components, and shared types.
|
|
7
|
+
* Server-only helpers live under `@cas-system/nextjs-cas-client/server`,
|
|
8
|
+
* route handlers under `/handlers`, and middleware under `/middleware`.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.CasProtectedRoute = exports.CasLoginButton = exports.useCasUser = exports.useCasAuth = exports.useCasAuthContext = exports.CasProvider = void 0;
|
|
12
|
+
// Provider + context hook
|
|
13
|
+
var CasProvider_js_1 = require("./CasProvider.js");
|
|
14
|
+
Object.defineProperty(exports, "CasProvider", { enumerable: true, get: function () { return CasProvider_js_1.CasProvider; } });
|
|
15
|
+
Object.defineProperty(exports, "useCasAuthContext", { enumerable: true, get: function () { return CasProvider_js_1.useCasAuthContext; } });
|
|
16
|
+
// Hooks
|
|
17
|
+
var useCasAuth_js_1 = require("./hooks/useCasAuth.js");
|
|
18
|
+
Object.defineProperty(exports, "useCasAuth", { enumerable: true, get: function () { return useCasAuth_js_1.useCasAuth; } });
|
|
19
|
+
var useCasUser_js_1 = require("./hooks/useCasUser.js");
|
|
20
|
+
Object.defineProperty(exports, "useCasUser", { enumerable: true, get: function () { return useCasUser_js_1.useCasUser; } });
|
|
21
|
+
// Components
|
|
22
|
+
var CasLoginButton_js_1 = require("./components/CasLoginButton.js");
|
|
23
|
+
Object.defineProperty(exports, "CasLoginButton", { enumerable: true, get: function () { return CasLoginButton_js_1.CasLoginButton; } });
|
|
24
|
+
var CasProtectedRoute_js_1 = require("./components/CasProtectedRoute.js");
|
|
25
|
+
Object.defineProperty(exports, "CasProtectedRoute", { enumerable: true, get: function () { return CasProtectedRoute_js_1.CasProtectedRoute; } });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,0BAA0B;AAC1B,mDAAkE;AAAzD,6GAAA,WAAW,OAAA;AAAE,mHAAA,iBAAiB,OAAA;AAGvC,QAAQ;AACR,uDAAmD;AAA1C,2GAAA,UAAU,OAAA;AACnB,uDAAmD;AAA1C,2GAAA,UAAU,OAAA;AAGnB,aAAa;AACb,oEAAgE;AAAvD,mHAAA,cAAc,OAAA;AAEvB,0EAAsE;AAA7D,yHAAA,iBAAiB,OAAA"}
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* };
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
23
|
-
import type { CasMiddlewareConfig } from './types';
|
|
22
|
+
import { NextRequest, NextResponse } from 'next/server.js';
|
|
23
|
+
import type { CasMiddlewareConfig } from './types.js';
|
|
24
24
|
/**
|
|
25
25
|
* Create a Next.js middleware function that enforces CAS authentication on
|
|
26
26
|
* the configured protected paths.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAatD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,IAQ3D,SAAS,WAAW,KACnB,OAAO,CAAC,YAAY,CAAC,CA+EzB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @cas-system/nextjs-cas-client/middleware
|
|
4
|
+
* @description Factory for creating a Next.js middleware that guards routes
|
|
5
|
+
* behind CAS authentication.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // middleware.ts (project root)
|
|
10
|
+
* import { createCasMiddleware } from '@cas-system/nextjs-cas-client/middleware';
|
|
11
|
+
*
|
|
12
|
+
* export default createCasMiddleware({
|
|
13
|
+
* protectedPaths: ['/dashboard', '/admin', '/settings'],
|
|
14
|
+
* publicPaths: ['/api/health'],
|
|
15
|
+
* loginPath: '/login',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export const config = {
|
|
19
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
20
|
+
* };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.createCasMiddleware = createCasMiddleware;
|
|
25
|
+
const server_js_1 = require("next/server.js");
|
|
26
|
+
const auth_js_1 = require("./server/auth.js");
|
|
27
|
+
/**
|
|
28
|
+
* Check whether `pathname` starts with any of the given prefixes.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
function matchesAny(pathname, prefixes) {
|
|
32
|
+
return prefixes.some((p) => pathname === p || pathname.startsWith(p.endsWith('/') ? p : `${p}/`));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a Next.js middleware function that enforces CAS authentication on
|
|
36
|
+
* the configured protected paths.
|
|
37
|
+
*
|
|
38
|
+
* @param config - Middleware configuration.
|
|
39
|
+
* @returns A Next.js-compatible middleware function.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* - If the request targets a **public path** it is always allowed through.
|
|
43
|
+
* - If the request targets a **protected path** and there is no valid CAS
|
|
44
|
+
* session cookie, the user is redirected to either the `loginPath` (if
|
|
45
|
+
* set) or the CAS server SSO login page.
|
|
46
|
+
* - The original URL is forwarded as a `?returnUrl=…` query parameter so
|
|
47
|
+
* the login page can redirect back after authentication.
|
|
48
|
+
*/
|
|
49
|
+
function createCasMiddleware(config) {
|
|
50
|
+
const { protectedPaths, publicPaths = [], loginPath, } = config;
|
|
51
|
+
return async function casMiddleware(request) {
|
|
52
|
+
const { pathname } = request.nextUrl;
|
|
53
|
+
// 1. Skip static assets and internal Next.js routes
|
|
54
|
+
if (pathname.startsWith('/_next') ||
|
|
55
|
+
pathname.startsWith('/favicon') ||
|
|
56
|
+
pathname.includes('.')) {
|
|
57
|
+
return server_js_1.NextResponse.next();
|
|
58
|
+
}
|
|
59
|
+
// 2. Always allow explicitly public paths
|
|
60
|
+
if (matchesAny(pathname, publicPaths)) {
|
|
61
|
+
return server_js_1.NextResponse.next();
|
|
62
|
+
}
|
|
63
|
+
// 3. Check whether this path requires authentication
|
|
64
|
+
const isProtected = matchesAny(pathname, protectedPaths);
|
|
65
|
+
if (!isProtected) {
|
|
66
|
+
return server_js_1.NextResponse.next();
|
|
67
|
+
}
|
|
68
|
+
// 4. Attempt to read the session from cookies
|
|
69
|
+
const session = await (0, auth_js_1.getCasSessionFromRequest)(request);
|
|
70
|
+
if (session) {
|
|
71
|
+
// Authenticated — attach user info as request headers so downstream
|
|
72
|
+
// Server Components / Route Handlers can read them without re-parsing
|
|
73
|
+
// the cookie.
|
|
74
|
+
const requestHeaders = new Headers(request.headers);
|
|
75
|
+
requestHeaders.set('x-cas-user', JSON.stringify(session.user));
|
|
76
|
+
requestHeaders.set('x-cas-authenticated', 'true');
|
|
77
|
+
return server_js_1.NextResponse.next({
|
|
78
|
+
request: { headers: requestHeaders },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// 5. Not authenticated — redirect to login
|
|
82
|
+
const returnUrl = request.nextUrl.pathname + request.nextUrl.search;
|
|
83
|
+
if (loginPath) {
|
|
84
|
+
// Redirect to the app's own login page
|
|
85
|
+
const loginUrl = new URL(loginPath, request.url);
|
|
86
|
+
loginUrl.searchParams.set('returnUrl', returnUrl);
|
|
87
|
+
return server_js_1.NextResponse.redirect(loginUrl);
|
|
88
|
+
}
|
|
89
|
+
// Redirect directly to the CAS SSO login page.
|
|
90
|
+
// The browser must reach CAS at its PUBLIC base url, which may differ from
|
|
91
|
+
// the internal back-channel url used for server-to-server validation.
|
|
92
|
+
// Prefer CAS_PUBLIC_URL; fall back to CAS_SERVER_URL for single-url setups.
|
|
93
|
+
const casServerUrl = process.env.CAS_PUBLIC_URL || process.env.CAS_SERVER_URL;
|
|
94
|
+
const casClientId = process.env.CAS_CLIENT_ID;
|
|
95
|
+
const casCallbackUrl = process.env.CAS_CALLBACK_URL ??
|
|
96
|
+
`${request.nextUrl.origin}/api/cas/callback`;
|
|
97
|
+
if (!casServerUrl || !casClientId) {
|
|
98
|
+
console.error('[CasMiddleware] CAS_SERVER_URL and CAS_CLIENT_ID env vars are required ' +
|
|
99
|
+
'when no loginPath is configured.');
|
|
100
|
+
return server_js_1.NextResponse.next();
|
|
101
|
+
}
|
|
102
|
+
// Encode the return URL into the callback so we can redirect after login
|
|
103
|
+
const callbackWithReturn = new URL(casCallbackUrl);
|
|
104
|
+
callbackWithReturn.searchParams.set('returnUrl', returnUrl);
|
|
105
|
+
const params = new URLSearchParams({
|
|
106
|
+
client_id: casClientId,
|
|
107
|
+
response_type: 'token',
|
|
108
|
+
redirect_uri: callbackWithReturn.toString(),
|
|
109
|
+
});
|
|
110
|
+
const casLoginUrl = `${casServerUrl.replace(/\/+$/, '')}/sso/login?${params.toString()}`;
|
|
111
|
+
return server_js_1.NextResponse.redirect(casLoginUrl);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - Token validation always happens server-side via {@link CasClient}.
|
|
14
14
|
*/
|
|
15
15
|
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';
|
|
16
|
-
import type { CasUser, CasSessionData } from '../types';
|
|
17
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
16
|
+
import type { CasUser, CasSessionData } from '../types.js';
|
|
17
|
+
import { NextRequest, NextResponse } from 'next/server.js';
|
|
18
18
|
/**
|
|
19
19
|
* Read the CAS session from Next.js **request** cookies.
|
|
20
20
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAC;AAC3G,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA8D3D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAuBhC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAGrE;AAMD,wDAAwD;AACxD,KAAK,YAAY,GAAG,CAClB,GAAG,EAAE,WAAW,EAChB,GAAG,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,KACtC,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEhE,8EAA8E;AAC9E,KAAK,oBAAoB,GAAG,CAC1B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACxC,IAAI,EAAE,OAAO,KACV,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,oBAAoB,GAAG,YAAY,CAsCvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAehC"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @cas-system/nextjs-cas-client/server/auth
|
|
4
|
+
* @description Server-side session helpers for reading / writing CAS session
|
|
5
|
+
* data to Next.js cookies.
|
|
6
|
+
*
|
|
7
|
+
* The session is stored as a base-64 encoded JSON blob (signed with HMAC)
|
|
8
|
+
* inside an `HttpOnly`, `Secure`, `SameSite=Lax` cookie. This gives us
|
|
9
|
+
* tamper-proof cookies without requiring any external crypto library.
|
|
10
|
+
*
|
|
11
|
+
* **Security model:**
|
|
12
|
+
* - The cookie is `HttpOnly` — JavaScript in the browser cannot read it.
|
|
13
|
+
* - A separate HMAC signature cookie prevents tampering.
|
|
14
|
+
* - Token validation always happens server-side via {@link CasClient}.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.getCasSession = getCasSession;
|
|
18
|
+
exports.setCasSession = setCasSession;
|
|
19
|
+
exports.clearCasSession = clearCasSession;
|
|
20
|
+
exports.withCasAuth = withCasAuth;
|
|
21
|
+
exports.getCasSessionFromRequest = getCasSessionFromRequest;
|
|
22
|
+
const server_js_1 = require("next/server.js");
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Constants
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/** Name of the cookie that stores the session payload. */
|
|
27
|
+
const SESSION_COOKIE = 'cas_session';
|
|
28
|
+
/** Name of the cookie that stores the HMAC signature of the payload. */
|
|
29
|
+
const SIGNATURE_COOKIE = 'cas_session_sig';
|
|
30
|
+
/** Secret used for HMAC signing — falls back to a build-time constant. */
|
|
31
|
+
function getSigningSecret() {
|
|
32
|
+
const secret = process.env.CAS_COOKIE_SECRET ?? process.env.CAS_CLIENT_SECRET;
|
|
33
|
+
if (!secret) {
|
|
34
|
+
throw new Error('[CasAuth] Neither CAS_COOKIE_SECRET nor CAS_CLIENT_SECRET environment variable is set. ' +
|
|
35
|
+
'One of these is required for cookie signing.');
|
|
36
|
+
}
|
|
37
|
+
return secret;
|
|
38
|
+
}
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Internal crypto helpers
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
/** @internal */
|
|
43
|
+
async function signPayload(payload, secret) {
|
|
44
|
+
const encoder = new TextEncoder();
|
|
45
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
46
|
+
const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
|
|
47
|
+
return btoa(String.fromCharCode(...new Uint8Array(sig)));
|
|
48
|
+
}
|
|
49
|
+
/** @internal */
|
|
50
|
+
async function verifyPayload(payload, signature, secret) {
|
|
51
|
+
const expected = await signPayload(payload, secret);
|
|
52
|
+
// Constant-time-ish comparison (good enough for HMAC output).
|
|
53
|
+
if (expected.length !== signature.length)
|
|
54
|
+
return false;
|
|
55
|
+
let mismatch = 0;
|
|
56
|
+
for (let i = 0; i < expected.length; i++) {
|
|
57
|
+
mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
58
|
+
}
|
|
59
|
+
return mismatch === 0;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Public API
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Read the CAS session from Next.js **request** cookies.
|
|
66
|
+
*
|
|
67
|
+
* Works in Server Components, Route Handlers, and middleware — anywhere
|
|
68
|
+
* you have access to the Next.js `cookies()` helper.
|
|
69
|
+
*
|
|
70
|
+
* @param cookies - The Next.js `cookies()` read-only store.
|
|
71
|
+
* @returns The session data, or `null` if no valid session exists.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* import { cookies } from 'next/headers';
|
|
76
|
+
* import { getCasSession } from '@cas-system/nextjs-cas-client/server';
|
|
77
|
+
*
|
|
78
|
+
* export default async function DashboardPage() {
|
|
79
|
+
* const session = await getCasSession(cookies());
|
|
80
|
+
* if (!session) redirect('/login');
|
|
81
|
+
* return <h1>Hello {session.user.username}</h1>;
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
async function getCasSession(cookies) {
|
|
86
|
+
try {
|
|
87
|
+
const sessionCookie = cookies.get(SESSION_COOKIE);
|
|
88
|
+
const sigCookie = cookies.get(SIGNATURE_COOKIE);
|
|
89
|
+
if (!sessionCookie?.value || !sigCookie?.value)
|
|
90
|
+
return null;
|
|
91
|
+
const secret = getSigningSecret();
|
|
92
|
+
const valid = await verifyPayload(sessionCookie.value, sigCookie.value, secret);
|
|
93
|
+
if (!valid) {
|
|
94
|
+
console.warn('[CasAuth] Cookie signature mismatch — session rejected.');
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const decoded = atob(sessionCookie.value);
|
|
98
|
+
const data = JSON.parse(decoded);
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Write the CAS session into Next.js **response** cookies.
|
|
107
|
+
*
|
|
108
|
+
* @param cookies - The Next.js `cookies()` store (must be writable — only
|
|
109
|
+
* available inside Route Handlers and Server Actions).
|
|
110
|
+
* @param user - The authenticated user.
|
|
111
|
+
* @param token - The raw JWT token.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { cookies } from 'next/headers';
|
|
116
|
+
* import { setCasSession } from '@cas-system/nextjs-cas-client/server';
|
|
117
|
+
*
|
|
118
|
+
* await setCasSession(cookies(), user, token);
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
async function setCasSession(cookies, user, token) {
|
|
122
|
+
const data = { user, token, createdAt: Date.now() };
|
|
123
|
+
const payload = btoa(JSON.stringify(data));
|
|
124
|
+
const secret = getSigningSecret();
|
|
125
|
+
const sig = await signPayload(payload, secret);
|
|
126
|
+
const cookieOptions = {
|
|
127
|
+
httpOnly: true,
|
|
128
|
+
secure: process.env.NODE_ENV === 'production',
|
|
129
|
+
sameSite: 'lax',
|
|
130
|
+
path: '/',
|
|
131
|
+
maxAge: 60 * 60 * 24 * 7, // 7 days
|
|
132
|
+
};
|
|
133
|
+
cookies.set(SESSION_COOKIE, payload, cookieOptions);
|
|
134
|
+
cookies.set(SIGNATURE_COOKIE, sig, cookieOptions);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Delete the CAS session cookies.
|
|
138
|
+
*
|
|
139
|
+
* @param cookies - The Next.js `cookies()` store.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* import { cookies } from 'next/headers';
|
|
144
|
+
* import { clearCasSession } from '@cas-system/nextjs-cas-client/server';
|
|
145
|
+
*
|
|
146
|
+
* clearCasSession(cookies());
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
function clearCasSession(cookies) {
|
|
150
|
+
cookies.delete(SESSION_COOKIE);
|
|
151
|
+
cookies.delete(SIGNATURE_COOKIE);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Higher-order function that wraps a Next.js App Router route handler,
|
|
155
|
+
* automatically extracting and verifying the CAS session and injecting the
|
|
156
|
+
* `CasUser` into the handler.
|
|
157
|
+
*
|
|
158
|
+
* If no valid session exists, responds with `401 Unauthorized`.
|
|
159
|
+
*
|
|
160
|
+
* @param handler - The protected route handler.
|
|
161
|
+
* @returns A standard Next.js route handler.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* // app/api/admin/route.ts
|
|
166
|
+
* import { withCasAuth } from '@cas-system/nextjs-cas-client/server';
|
|
167
|
+
*
|
|
168
|
+
* export const GET = withCasAuth(async (req, ctx, user) => {
|
|
169
|
+
* return NextResponse.json({ message: `Hello ${user.username}` });
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
function withCasAuth(handler) {
|
|
174
|
+
return async (req, ctx) => {
|
|
175
|
+
// In App Router route handlers, we can read cookies directly from the request.
|
|
176
|
+
const sessionCookie = req.cookies.get(SESSION_COOKIE);
|
|
177
|
+
const sigCookie = req.cookies.get(SIGNATURE_COOKIE);
|
|
178
|
+
if (!sessionCookie?.value || !sigCookie?.value) {
|
|
179
|
+
return server_js_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const secret = getSigningSecret();
|
|
183
|
+
const valid = await verifyPayload(sessionCookie.value, sigCookie.value, secret);
|
|
184
|
+
if (!valid) {
|
|
185
|
+
return server_js_1.NextResponse.json({ error: 'Unauthorized — invalid session' }, { status: 401 });
|
|
186
|
+
}
|
|
187
|
+
const decoded = atob(sessionCookie.value);
|
|
188
|
+
const data = JSON.parse(decoded);
|
|
189
|
+
return handler(req, ctx ?? {}, data.user);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return server_js_1.NextResponse.json({ error: 'Unauthorized — session parse error' }, { status: 401 });
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Read the CAS session from a plain `NextRequest` (useful in middleware
|
|
198
|
+
* and edge functions where the `cookies()` helper is not available).
|
|
199
|
+
*
|
|
200
|
+
* @param request - The incoming Next.js request.
|
|
201
|
+
* @returns Session data or `null`.
|
|
202
|
+
*
|
|
203
|
+
* @internal
|
|
204
|
+
*/
|
|
205
|
+
async function getCasSessionFromRequest(request) {
|
|
206
|
+
try {
|
|
207
|
+
const sessionValue = request.cookies.get(SESSION_COOKIE)?.value;
|
|
208
|
+
const sigValue = request.cookies.get(SIGNATURE_COOKIE)?.value;
|
|
209
|
+
if (!sessionValue || !sigValue)
|
|
210
|
+
return null;
|
|
211
|
+
const secret = getSigningSecret();
|
|
212
|
+
const valid = await verifyPayload(sessionValue, sigValue, secret);
|
|
213
|
+
if (!valid)
|
|
214
|
+
return null;
|
|
215
|
+
const decoded = atob(sessionValue);
|
|
216
|
+
return JSON.parse(decoded);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/server/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* **IMPORTANT:** This module must only run on the server. It requires the
|
|
8
8
|
* `clientSecret` which must never be sent to the browser.
|
|
9
9
|
*/
|
|
10
|
-
import type { CasServerConfig, CasUser } from '../types';
|
|
10
|
+
import type { CasServerConfig, CasUser } from '../types.js';
|
|
11
11
|
/**
|
|
12
12
|
* Server-side CAS client.
|
|
13
13
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cas-client.d.ts","sourceRoot":"","sources":["../../../src/server/cas-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EAGR,MAAM,aAAa,CAAC;AAkBrB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAErD,MAAM,EAAE,eAAe;IAgBnC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAevC;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAuC3D;;;;;;;;OAQG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuC7D;;;;;OAKG;IACG,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgC9C;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;IAKxD;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;CAI3D"}
|