@nibssplc/cams-sdk-react 0.0.1-beta.78 → 0.0.1-beta.79
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/README.md +295 -0
- package/dist/components/ADLoginModal.d.ts +7 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/hooks/useCAMSAuth.d.ts +2 -0
- package/dist/index.cjs.js +137 -29
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +137 -30
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# @nibssplc/cams-sdk-react
|
|
2
|
+
|
|
3
|
+
React hooks and components for NIBSS CAMS SDK with support for both regular CAMS authentication and Azure AD MSAL integration.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @nibssplc/cams-sdk-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For MSAL mode, also install:
|
|
12
|
+
```bash
|
|
13
|
+
npm install @azure/msal-browser @azure/msal-react
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- ⚛️ React hooks for authentication
|
|
19
|
+
- 🔄 Unified provider supporting both REGULAR and MSAL modes
|
|
20
|
+
- 🎨 Pre-built UI components (login page, MFA gate)
|
|
21
|
+
- 🛡️ Protected route component
|
|
22
|
+
- 🎯 TypeScript support
|
|
23
|
+
- 🪝 Context-based state management
|
|
24
|
+
- 📱 Next.js compatible
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### MSAL Mode (Azure AD + MFA)
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { UnifiedCAMSProvider, useCAMSContext } from '@nibssplc/cams-sdk-react';
|
|
32
|
+
|
|
33
|
+
const msalConfig = {
|
|
34
|
+
auth: {
|
|
35
|
+
clientId: 'your-client-id',
|
|
36
|
+
authority: 'https://login.microsoftonline.com/your-tenant-id',
|
|
37
|
+
redirectUri: window.location.origin
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
return (
|
|
43
|
+
<UnifiedCAMSProvider
|
|
44
|
+
mode="MSAL"
|
|
45
|
+
appCode="your-app-guid"
|
|
46
|
+
msalConfig={msalConfig}
|
|
47
|
+
MFAEndpoint="https://your-mfa-endpoint.com"
|
|
48
|
+
>
|
|
49
|
+
<YourApp />
|
|
50
|
+
</UnifiedCAMSProvider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function YourApp() {
|
|
55
|
+
const { login, logout, isAuthenticated, userProfile } = useCAMSContext();
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div>
|
|
59
|
+
{isAuthenticated ? (
|
|
60
|
+
<>
|
|
61
|
+
<p>Welcome, {userProfile?.name}</p>
|
|
62
|
+
<button onClick={logout}>Logout</button>
|
|
63
|
+
</>
|
|
64
|
+
) : (
|
|
65
|
+
<button onClick={login}>Login</button>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### REGULAR Mode (Popup Authentication)
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { UnifiedCAMSProvider, useCAMSContext } from '@nibssplc/cams-sdk-react';
|
|
76
|
+
|
|
77
|
+
function App() {
|
|
78
|
+
return (
|
|
79
|
+
<UnifiedCAMSProvider
|
|
80
|
+
mode="REGULAR"
|
|
81
|
+
appCode="your-app-guid"
|
|
82
|
+
camsUrl="https://your-cams-url.com"
|
|
83
|
+
messageOrigin="https://your-cams-url.com"
|
|
84
|
+
windowWidth={500}
|
|
85
|
+
windowHeight={600}
|
|
86
|
+
>
|
|
87
|
+
<YourApp />
|
|
88
|
+
</UnifiedCAMSProvider>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Components
|
|
94
|
+
|
|
95
|
+
### UnifiedCAMSProvider
|
|
96
|
+
|
|
97
|
+
Main provider component supporting both authentication modes.
|
|
98
|
+
|
|
99
|
+
**Props (MSAL Mode):**
|
|
100
|
+
```tsx
|
|
101
|
+
interface MSALProviderProps {
|
|
102
|
+
mode: "MSAL";
|
|
103
|
+
appCode: string;
|
|
104
|
+
msalConfig: Configuration;
|
|
105
|
+
msalInstance?: PublicClientApplication;
|
|
106
|
+
MFAEndpoint: string;
|
|
107
|
+
children: React.ReactNode;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Props (REGULAR Mode):**
|
|
112
|
+
```tsx
|
|
113
|
+
interface RegularProviderProps {
|
|
114
|
+
mode: "REGULAR";
|
|
115
|
+
appCode: string;
|
|
116
|
+
camsUrl: string;
|
|
117
|
+
messageOrigin: string;
|
|
118
|
+
windowWidth: number;
|
|
119
|
+
windowHeight: number;
|
|
120
|
+
authTimeout?: number;
|
|
121
|
+
debug?: boolean;
|
|
122
|
+
children: React.ReactNode;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### DefaultLoginPage
|
|
127
|
+
|
|
128
|
+
Pre-built login page with Microsoft and AD authentication options.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { DefaultLoginPage } from '@nibssplc/cams-sdk-react';
|
|
132
|
+
|
|
133
|
+
function App() {
|
|
134
|
+
return (
|
|
135
|
+
<UnifiedCAMSProvider mode="MSAL" {...config}>
|
|
136
|
+
<DefaultLoginPage />
|
|
137
|
+
</UnifiedCAMSProvider>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### MFAGate
|
|
143
|
+
|
|
144
|
+
Component that enforces MFA completion before rendering children.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { MFAGate } from '@nibssplc/cams-sdk-react';
|
|
148
|
+
|
|
149
|
+
function ProtectedContent() {
|
|
150
|
+
return (
|
|
151
|
+
<MFAGate>
|
|
152
|
+
<YourProtectedContent />
|
|
153
|
+
</MFAGate>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### ProtectedRoute
|
|
159
|
+
|
|
160
|
+
Route wrapper that requires authentication.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { ProtectedRoute } from '@nibssplc/cams-sdk-react';
|
|
164
|
+
|
|
165
|
+
function App() {
|
|
166
|
+
return (
|
|
167
|
+
<ProtectedRoute>
|
|
168
|
+
<YourProtectedPage />
|
|
169
|
+
</ProtectedRoute>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Hooks
|
|
175
|
+
|
|
176
|
+
### useCAMSContext
|
|
177
|
+
|
|
178
|
+
Access authentication state and methods.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useCAMSContext } from '@nibssplc/cams-sdk-react';
|
|
182
|
+
|
|
183
|
+
function MyComponent() {
|
|
184
|
+
const {
|
|
185
|
+
isAuthenticated,
|
|
186
|
+
isLoading,
|
|
187
|
+
userProfile,
|
|
188
|
+
login,
|
|
189
|
+
logout,
|
|
190
|
+
error,
|
|
191
|
+
authMode
|
|
192
|
+
} = useCAMSContext();
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div>
|
|
196
|
+
{isAuthenticated && <p>Hello, {userProfile?.name}</p>}
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Return Values:**
|
|
203
|
+
|
|
204
|
+
| Property | Type | Description |
|
|
205
|
+
|----------|------|-------------|
|
|
206
|
+
| `isAuthenticated` | boolean | Authentication status |
|
|
207
|
+
| `isLoading` | boolean | Loading state |
|
|
208
|
+
| `userProfile` | Profile \| null | User profile data |
|
|
209
|
+
| `login` | function | Trigger login flow |
|
|
210
|
+
| `logout` | function | Logout user |
|
|
211
|
+
| `error` | Error \| null | Authentication error |
|
|
212
|
+
| `authMode` | 'REGULAR' \| 'MSAL' | Current auth mode |
|
|
213
|
+
| `requiresMFA` | boolean | MFA required (MSAL only) |
|
|
214
|
+
|
|
215
|
+
### useCAMSAuth
|
|
216
|
+
|
|
217
|
+
Hook for REGULAR mode authentication.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { useCAMSAuth } from '@nibssplc/cams-sdk-react';
|
|
221
|
+
|
|
222
|
+
const auth = useCAMSAuth({
|
|
223
|
+
appCode: 'your-app-guid',
|
|
224
|
+
camsUrl: 'https://your-cams-url.com',
|
|
225
|
+
messageOrigin: 'https://your-cams-url.com',
|
|
226
|
+
windowWidth: 500,
|
|
227
|
+
windowHeight: 600
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### useCAMSMSALAuth
|
|
232
|
+
|
|
233
|
+
Hook for MSAL mode authentication.
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { useCAMSMSALAuth } from '@nibssplc/cams-sdk-react';
|
|
237
|
+
|
|
238
|
+
const auth = useCAMSMSALAuth({
|
|
239
|
+
appCode: 'your-app-guid',
|
|
240
|
+
MFAEndpoint: 'https://your-mfa-endpoint.com'
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Next.js Integration
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
// app/providers.tsx
|
|
248
|
+
'use client';
|
|
249
|
+
|
|
250
|
+
import { UnifiedCAMSProvider } from '@nibssplc/cams-sdk-react';
|
|
251
|
+
|
|
252
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
253
|
+
return (
|
|
254
|
+
<UnifiedCAMSProvider mode="MSAL" {...config}>
|
|
255
|
+
{children}
|
|
256
|
+
</UnifiedCAMSProvider>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// app/layout.tsx
|
|
261
|
+
import { Providers } from './providers';
|
|
262
|
+
|
|
263
|
+
export default function RootLayout({ children }) {
|
|
264
|
+
return (
|
|
265
|
+
<html>
|
|
266
|
+
<body>
|
|
267
|
+
<Providers>{children}</Providers>
|
|
268
|
+
</body>
|
|
269
|
+
</html>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Styling
|
|
275
|
+
|
|
276
|
+
Components use Tailwind CSS. Ensure Tailwind is configured in your project:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm install -D tailwindcss
|
|
280
|
+
npx tailwindcss init
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Examples
|
|
284
|
+
|
|
285
|
+
See the [examples](../../examples) directory for complete implementations:
|
|
286
|
+
- `integrated-mfa-example.tsx` - MSAL mode with MFA
|
|
287
|
+
- `popup-auth-example.tsx` - Regular popup authentication
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT
|
|
292
|
+
|
|
293
|
+
## Author
|
|
294
|
+
|
|
295
|
+
NIBSS PLC, Caleb Boluwade
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface ADLoginModalProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onOpenChange: (open: boolean) => void;
|
|
4
|
+
onLogin: (username: string, password: string, mfaCode: string) => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare const ADLoginModal: ({ open, onOpenChange, onLogin }: ADLoginModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -7,6 +7,7 @@ export interface UseCAMSAuthOptions {
|
|
|
7
7
|
onTokenExpired?: () => void;
|
|
8
8
|
autoClose?: boolean;
|
|
9
9
|
idleTimeout?: number;
|
|
10
|
+
loginExpiry?: number;
|
|
10
11
|
}
|
|
11
12
|
export interface UseCAMSAuthReturn {
|
|
12
13
|
login: (config: CAMSConfig) => Promise<void>;
|
|
@@ -18,5 +19,6 @@ export interface UseCAMSAuthReturn {
|
|
|
18
19
|
profile: Profile | null;
|
|
19
20
|
appCode: string;
|
|
20
21
|
storageKey: string;
|
|
22
|
+
loginExpiry: number;
|
|
21
23
|
}
|
|
22
24
|
export declare function useCAMSAuth(options?: UseCAMSAuthOptions): UseCAMSAuthReturn;
|
package/dist/index.cjs.js
CHANGED
|
@@ -12,7 +12,7 @@ var resolvers = require('@hookform/resolvers');
|
|
|
12
12
|
var clsx = require('clsx');
|
|
13
13
|
var tailwindMerge = require('tailwind-merge');
|
|
14
14
|
var reactSlot = require('@radix-ui/react-slot');
|
|
15
|
-
require('@radix-ui/react-label');
|
|
15
|
+
var LabelPrimitive = require('@radix-ui/react-label');
|
|
16
16
|
var classVarianceAuthority = require('class-variance-authority');
|
|
17
17
|
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
18
18
|
var sonner = require('sonner');
|
|
@@ -37,6 +37,7 @@ function _interopNamespaceDefault(e) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
40
|
+
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespaceDefault(LabelPrimitive);
|
|
40
41
|
var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespaceDefault(DialogPrimitive);
|
|
41
42
|
|
|
42
43
|
/******************************************************************************
|
|
@@ -134,15 +135,17 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
134
135
|
|
|
135
136
|
function useCAMSAuth(options) {
|
|
136
137
|
var _this = this;
|
|
138
|
+
var _a;
|
|
137
139
|
if (options === void 0) { options = {
|
|
138
140
|
appCode: "",
|
|
141
|
+
loginExpiry: 1,
|
|
139
142
|
storageKey: "CAMS-AUTH-SDK",
|
|
140
143
|
}; }
|
|
141
|
-
var
|
|
142
|
-
var
|
|
143
|
-
var
|
|
144
|
-
var
|
|
145
|
-
var
|
|
144
|
+
var _b = React.useState(false), isAuthenticated = _b[0], setIsAuthenticated = _b[1];
|
|
145
|
+
var _c = React.useState(false), isLoading = _c[0], setIsLoading = _c[1];
|
|
146
|
+
var _d = React.useState(null), error = _d[0], setError = _d[1];
|
|
147
|
+
var _e = React.useState(""), token = _e[0], setToken = _e[1];
|
|
148
|
+
var _f = React.useState(null), profile = _f[0], setProfile = _f[1];
|
|
146
149
|
var sessionManagerRef = React.useRef(null);
|
|
147
150
|
React.useEffect(function () {
|
|
148
151
|
var _a, _b;
|
|
@@ -153,7 +156,7 @@ function useCAMSAuth(options) {
|
|
|
153
156
|
if (camsSdk.isPopupWindow()) {
|
|
154
157
|
camsSdk.initializePopupAuth();
|
|
155
158
|
}
|
|
156
|
-
(_a = sessionManagerRef.current) !== null && _a !== void 0 ? _a : (sessionManagerRef.current = new camsSdk.CAMSSessionManager(localStorage, options.storageKey || "CAMS-SDK", {
|
|
159
|
+
(_a = sessionManagerRef.current) !== null && _a !== void 0 ? _a : (sessionManagerRef.current = new camsSdk.CAMSSessionManager(localStorage, options.storageKey || "CAMS-AUTH-SDK", {
|
|
157
160
|
onAuthSuccess: function (res) {
|
|
158
161
|
var _a;
|
|
159
162
|
setToken(res.userProfile.tokens.Bearer);
|
|
@@ -189,40 +192,53 @@ function useCAMSAuth(options) {
|
|
|
189
192
|
}
|
|
190
193
|
}, [options.storageKey]);
|
|
191
194
|
var login = React.useCallback(function (config) { return __awaiter(_this, void 0, void 0, function () {
|
|
192
|
-
var loginConfig, userProfile, err_1;
|
|
193
|
-
var _a;
|
|
194
|
-
return __generator(this, function (
|
|
195
|
-
switch (
|
|
195
|
+
var loginConfig, userProfile, err_1, e, isPopupClosedError, restoredToken, userProfile;
|
|
196
|
+
var _a, _b, _c;
|
|
197
|
+
return __generator(this, function (_d) {
|
|
198
|
+
switch (_d.label) {
|
|
196
199
|
case 0:
|
|
197
200
|
if (!sessionManagerRef.current)
|
|
198
201
|
return [2 /*return*/];
|
|
199
202
|
setIsLoading(true);
|
|
200
203
|
setError(null);
|
|
201
|
-
|
|
204
|
+
_d.label = 1;
|
|
202
205
|
case 1:
|
|
203
|
-
|
|
204
|
-
loginConfig = __assign(__assign({}, config), {
|
|
205
|
-
// autoClose: options.autoClose ?? config.autoClose,
|
|
206
|
-
idleTimeout: (_a = options.idleTimeout) !== null && _a !== void 0 ? _a : config.idleTimeout });
|
|
206
|
+
_d.trys.push([1, 4, 8, 9]);
|
|
207
|
+
loginConfig = __assign(__assign({}, config), { idleTimeout: options.idleTimeout });
|
|
207
208
|
return [4 /*yield*/, sessionManagerRef.current.login(loginConfig)];
|
|
208
209
|
case 2:
|
|
209
|
-
|
|
210
|
+
_d.sent();
|
|
210
211
|
return [4 /*yield*/, sessionManagerRef.current.getProfile()];
|
|
211
212
|
case 3:
|
|
212
|
-
userProfile =
|
|
213
|
+
userProfile = _d.sent();
|
|
213
214
|
setProfile(userProfile);
|
|
214
|
-
return [3 /*break*/,
|
|
215
|
+
return [3 /*break*/, 9];
|
|
215
216
|
case 4:
|
|
216
|
-
err_1 =
|
|
217
|
+
err_1 = _d.sent();
|
|
218
|
+
e = err_1;
|
|
219
|
+
isPopupClosedError = ((_a = e === null || e === void 0 ? void 0 : e.message) === null || _a === void 0 ? void 0 : _a.includes("Authentication window was closed")) ||
|
|
220
|
+
(e === null || e === void 0 ? void 0 : e.error) === "Authentication window was closed";
|
|
221
|
+
if (!(isPopupClosedError && sessionManagerRef.current.isAuthenticated())) return [3 /*break*/, 6];
|
|
222
|
+
restoredToken = (_b = sessionManagerRef.current.getAccessToken()) !== null && _b !== void 0 ? _b : "";
|
|
223
|
+
setToken(restoredToken);
|
|
224
|
+
setIsAuthenticated(true);
|
|
225
|
+
return [4 /*yield*/, sessionManagerRef.current.getProfile()];
|
|
226
|
+
case 5:
|
|
227
|
+
userProfile = _d.sent();
|
|
228
|
+
setProfile(userProfile);
|
|
229
|
+
(_c = options.onAuthSuccess) === null || _c === void 0 ? void 0 : _c.call(options, restoredToken);
|
|
230
|
+
return [3 /*break*/, 7];
|
|
231
|
+
case 6:
|
|
217
232
|
setError(err_1);
|
|
218
233
|
setIsAuthenticated(false);
|
|
219
234
|
setToken("");
|
|
220
235
|
setProfile(null);
|
|
221
|
-
|
|
222
|
-
case
|
|
236
|
+
_d.label = 7;
|
|
237
|
+
case 7: return [3 /*break*/, 9];
|
|
238
|
+
case 8:
|
|
223
239
|
setIsLoading(false);
|
|
224
240
|
return [7 /*endfinally*/];
|
|
225
|
-
case
|
|
241
|
+
case 9: return [2 /*return*/];
|
|
226
242
|
}
|
|
227
243
|
});
|
|
228
244
|
}); }, [options.idleTimeout]);
|
|
@@ -251,8 +267,9 @@ function useCAMSAuth(options) {
|
|
|
251
267
|
error: error,
|
|
252
268
|
token: token,
|
|
253
269
|
profile: profile,
|
|
270
|
+
loginExpiry: (_a = options.loginExpiry) !== null && _a !== void 0 ? _a : 1,
|
|
254
271
|
appCode: options.appCode,
|
|
255
|
-
storageKey: options.storageKey || "CAMS-SDK",
|
|
272
|
+
storageKey: options.storageKey || "CAMS-AUTH-SDK",
|
|
256
273
|
};
|
|
257
274
|
}
|
|
258
275
|
|
|
@@ -1418,6 +1435,11 @@ function InputOTPSlot(_a) {
|
|
|
1418
1435
|
return (jsxRuntimeExports.jsxs("div", __assign({ "data-slot": "input-otp-slot", "data-active": isActive, className: cn("data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-12 w-12 items-center justify-center border-y border-r text-lg font-semibold shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]", className) }, props, { children: [char, hasFakeCaret && (jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: jsxRuntimeExports.jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px duration-1000" }) }))] })));
|
|
1419
1436
|
}
|
|
1420
1437
|
|
|
1438
|
+
function Label(_a) {
|
|
1439
|
+
var className = _a.className, props = __rest(_a, ["className"]);
|
|
1440
|
+
return (jsxRuntimeExports.jsx(LabelPrimitive__namespace.Root, __assign({ "data-slot": "label", className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className) }, props)));
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1421
1443
|
var Form = reactHookForm.FormProvider;
|
|
1422
1444
|
var FormFieldContext = React__namespace.createContext({});
|
|
1423
1445
|
var FormField = function (_a) {
|
|
@@ -1442,6 +1464,11 @@ function FormItem(_a) {
|
|
|
1442
1464
|
var id = React__namespace.useId();
|
|
1443
1465
|
return (jsxRuntimeExports.jsx(FormItemContext.Provider, { value: { id: id }, children: jsxRuntimeExports.jsx("div", __assign({ "data-slot": "form-item", className: cn("grid gap-2", className) }, props)) }));
|
|
1444
1466
|
}
|
|
1467
|
+
function FormLabel(_a) {
|
|
1468
|
+
var className = _a.className, props = __rest(_a, ["className"]);
|
|
1469
|
+
var _b = useFormField(), error = _b.error, formItemId = _b.formItemId;
|
|
1470
|
+
return (jsxRuntimeExports.jsx(Label, __assign({ "data-slot": "form-label", "data-error": !!error, className: cn("data-[error=true]:text-destructive", className), htmlFor: formItemId }, props)));
|
|
1471
|
+
}
|
|
1445
1472
|
function FormControl(_a) {
|
|
1446
1473
|
var props = __rest(_a, []);
|
|
1447
1474
|
var _b = useFormField(), error = _b.error, formItemId = _b.formItemId, formDescriptionId = _b.formDescriptionId, formMessageId = _b.formMessageId;
|
|
@@ -1861,9 +1888,80 @@ var AuthLogo = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2
|
|
|
1861
1888
|
|
|
1862
1889
|
var MicrosoftLogo = "data:image/svg+xml,%3Csvg%20width%3D%2225%22%20height%3D%2225%22%20viewBox%3D%220%200%2025%2025%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20id%3D%22microsoft_svgrepo.com%22%3E%3Cpath%20id%3D%22Vector%22%20d%3D%22M20.75%2013.25H13.25V20.75H20.75V13.25Z%22%20fill%3D%22%23FEBA08%22%2F%3E%3Cpath%20id%3D%22Vector_2%22%20d%3D%22M11.75%2013.25H4.25V20.75H11.75V13.25Z%22%20fill%3D%22%2305A6F0%22%2F%3E%3Cpath%20id%3D%22Vector_3%22%20d%3D%22M20.75%204.25H13.25V11.75H20.75V4.25Z%22%20fill%3D%22%2380BC06%22%2F%3E%3Cpath%20id%3D%22Vector_4%22%20d%3D%22M11.75%204.25H4.25V11.75H11.75V4.25Z%22%20fill%3D%22%23F25325%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E";
|
|
1863
1890
|
|
|
1891
|
+
var Input = React__namespace.forwardRef(function (_a, ref) {
|
|
1892
|
+
var className = _a.className, type = _a.type, props = __rest(_a, ["className", "type"]);
|
|
1893
|
+
return (jsxRuntimeExports.jsx("input", __assign({ type: type, className: cn("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className), ref: ref }, props)));
|
|
1894
|
+
});
|
|
1895
|
+
Input.displayName = "Input";
|
|
1896
|
+
|
|
1897
|
+
var credentialsSchema = z.z.object({
|
|
1898
|
+
username: z.z.string().min(1, "Username is required"),
|
|
1899
|
+
password: z.z.string().min(1, "Password is required"),
|
|
1900
|
+
});
|
|
1901
|
+
var ADLoginModal = function (_a) {
|
|
1902
|
+
var open = _a.open, onOpenChange = _a.onOpenChange, onLogin = _a.onLogin;
|
|
1903
|
+
var _b = React.useState("credentials"), step = _b[0], setStep = _b[1];
|
|
1904
|
+
var _c = React.useState({ username: "", password: "" }), credentials = _c[0], setCredentials = _c[1];
|
|
1905
|
+
var _d = React.useState(""), mfaCode = _d[0], setMfaCode = _d[1];
|
|
1906
|
+
var _e = React.useState(false), isLoading = _e[0], setIsLoading = _e[1];
|
|
1907
|
+
var form = reactHookForm.useForm({
|
|
1908
|
+
resolver: a$1(credentialsSchema),
|
|
1909
|
+
defaultValues: { username: "", password: "" },
|
|
1910
|
+
});
|
|
1911
|
+
var handleCredentialsSubmit = function (values) { return __awaiter(void 0, void 0, void 0, function () {
|
|
1912
|
+
return __generator(this, function (_a) {
|
|
1913
|
+
setCredentials(values);
|
|
1914
|
+
setStep("mfa");
|
|
1915
|
+
return [2 /*return*/];
|
|
1916
|
+
});
|
|
1917
|
+
}); };
|
|
1918
|
+
var handleMFASubmit = function (code) { return __awaiter(void 0, void 0, void 0, function () {
|
|
1919
|
+
var error_1;
|
|
1920
|
+
return __generator(this, function (_a) {
|
|
1921
|
+
switch (_a.label) {
|
|
1922
|
+
case 0:
|
|
1923
|
+
setIsLoading(true);
|
|
1924
|
+
_a.label = 1;
|
|
1925
|
+
case 1:
|
|
1926
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
1927
|
+
return [4 /*yield*/, onLogin(credentials.username, credentials.password, code)];
|
|
1928
|
+
case 2:
|
|
1929
|
+
_a.sent();
|
|
1930
|
+
onOpenChange(false);
|
|
1931
|
+
setStep("credentials");
|
|
1932
|
+
form.reset();
|
|
1933
|
+
setMfaCode("");
|
|
1934
|
+
return [3 /*break*/, 5];
|
|
1935
|
+
case 3:
|
|
1936
|
+
error_1 = _a.sent();
|
|
1937
|
+
console.error("AD login failed:", error_1);
|
|
1938
|
+
return [3 /*break*/, 5];
|
|
1939
|
+
case 4:
|
|
1940
|
+
setIsLoading(false);
|
|
1941
|
+
return [7 /*endfinally*/];
|
|
1942
|
+
case 5: return [2 /*return*/];
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
}); };
|
|
1946
|
+
var handleClose = function () {
|
|
1947
|
+
onOpenChange(false);
|
|
1948
|
+
setStep("credentials");
|
|
1949
|
+
form.reset();
|
|
1950
|
+
setMfaCode("");
|
|
1951
|
+
};
|
|
1952
|
+
return (jsxRuntimeExports.jsx(Dialog, { open: open, onOpenChange: handleClose, children: jsxRuntimeExports.jsxs(DialogContent, { className: "sm:max-w-md", children: [jsxRuntimeExports.jsx(DialogHeader, { children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(lucideReact.KeyIcon, { className: "w-5 h-5 text-[#506f4a]" }), jsxRuntimeExports.jsx(DialogTitle, { children: "Sign in with AD" })] }) }), step === "credentials" ? (jsxRuntimeExports.jsx(Form, __assign({}, form, { children: jsxRuntimeExports.jsxs("form", { onSubmit: form.handleSubmit(handleCredentialsSubmit), className: "space-y-4", children: [jsxRuntimeExports.jsx(FormField, { control: form.control, name: "username", render: function (_a) {
|
|
1953
|
+
var field = _a.field;
|
|
1954
|
+
return (jsxRuntimeExports.jsxs(FormItem, { children: [jsxRuntimeExports.jsx(FormLabel, { children: "Username" }), jsxRuntimeExports.jsx(FormControl, { children: jsxRuntimeExports.jsx(Input, __assign({ placeholder: "Enter your username" }, field)) }), jsxRuntimeExports.jsx(FormMessage, {})] }));
|
|
1955
|
+
} }), jsxRuntimeExports.jsx(FormField, { control: form.control, name: "password", render: function (_a) {
|
|
1956
|
+
var field = _a.field;
|
|
1957
|
+
return (jsxRuntimeExports.jsxs(FormItem, { children: [jsxRuntimeExports.jsx(FormLabel, { children: "Password" }), jsxRuntimeExports.jsx(FormControl, { children: jsxRuntimeExports.jsx(Input, __assign({ type: "password", placeholder: "Enter your password" }, field)) }), jsxRuntimeExports.jsx(FormMessage, {})] }));
|
|
1958
|
+
} }), jsxRuntimeExports.jsx(Button, { type: "submit", className: "w-full bg-[#506f4a] hover:bg-[#506f4a]/90", children: "Continue" })] }) }))) : (jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [jsxRuntimeExports.jsx(GenericOTPVerifier, { value: mfaCode, setValue: setMfaCode, setLoading: setIsLoading, isDisabled: isLoading, onChangeOTP: handleMFASubmit, fieldName: "AuthenticatorCode" }), isLoading && (jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-2 text-sm text-muted-foreground", children: [jsxRuntimeExports.jsx(lucideReact.Loader2, { className: "w-4 h-4 animate-spin" }), jsxRuntimeExports.jsx("span", { children: "Verifying..." })] }))] }))] }) }));
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1864
1961
|
var DefaultLoginPage = function () {
|
|
1865
1962
|
var context = useCAMSContext$1();
|
|
1866
1963
|
var login = context.login, isLoading = context.isLoading, authMode = context.authMode;
|
|
1964
|
+
var _a = React.useState(false), showADModal = _a[0], setShowADModal = _a[1];
|
|
1867
1965
|
var handleLogin = function () {
|
|
1868
1966
|
if (authMode === "MSAL") {
|
|
1869
1967
|
login();
|
|
@@ -1883,11 +1981,21 @@ var DefaultLoginPage = function () {
|
|
|
1883
1981
|
},
|
|
1884
1982
|
exit: { opacity: 0, scale: 0.8, y: -50, transition: { duration: 0.3 } },
|
|
1885
1983
|
};
|
|
1886
|
-
return (jsxRuntimeExports.
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1984
|
+
return (jsxRuntimeExports.jsxs("main", { className: "min-h-screen bg-gray-50", children: [jsxRuntimeExports.jsx(framerMotion.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.5 }, children: jsxRuntimeExports.jsx("div", { className: "flex h-screen items-center justify-center", children: jsxRuntimeExports.jsxs(framerMotion.motion.div, { variants: cardVariants, initial: "hidden", animate: "visible", exit: "exit", className: "w-full max-w-md p-6 space-y-4 bg-gray-50 rounded-2xl shadow-2xl --dark:bg-gray-800", children: [jsxRuntimeExports.jsxs(CardHeader, { className: "text-center space-y-3", children: [jsxRuntimeExports.jsx("div", { className: "w-full flex items-center justify-center", children: jsxRuntimeExports.jsx("img", { src: NIBSSLogo, alt: "NIBSS Logo", width: 265, height: 265 }) }), jsxRuntimeExports.jsx(CardTitle, { className: "text-3xl font-bold --text-gray-900 --dark:text-white", children: "NIBSS CAMS" }), jsxRuntimeExports.jsx(CardTitle, { className: "text-gray-500 dark:text-gray-400 font-bold text-lg", children: "Centralized Authentication" })] }), jsxRuntimeExports.jsxs(CardAction, { className: "w-full flex flex-col items-center justify-center text-center text-gray-500 dark:text-gray-400 mb-4", children: [jsxRuntimeExports.jsx("img", { src: AuthLogo, alt: "Auth Logo", width: 365, height: 365 }), "Kindly use the below identity providers to authenticate"] }), jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [jsxRuntimeExports.jsxs(Button
|
|
1985
|
+
// variant="outline"
|
|
1986
|
+
, {
|
|
1987
|
+
// variant="outline"
|
|
1988
|
+
className: "w-full flex items-center justify-center cursor-pointer bg-[#506f4a] hover:bg-[#506f4a] rounded-lg border border-transparent px-5 py-8 text-base font-medium transition-colors duration-250", onClick: handleLogin, disabled: isLoading, children: [jsxRuntimeExports.jsx("img", { src: MicrosoftLogo, alt: "Microsoft Logo", width: 35, height: 35 }), jsxRuntimeExports.jsx("span", { children: isLoading ? "Logging in..." : "Sign in with Microsoft" })] }), jsxRuntimeExports.jsxs(Button
|
|
1989
|
+
// variant="outline"
|
|
1990
|
+
, {
|
|
1991
|
+
// variant="outline"
|
|
1992
|
+
className: "w-full flex items-center justify-center cursor-pointer bg-[#506f4a] hover:bg-[#506f4a] rounded-lg border border-transparent px-5 py-8 text-base font-medium transition-colors duration-250", onClick: function () { return setShowADModal(true); }, disabled: isLoading, children: [jsxRuntimeExports.jsx(lucideReact.KeyIcon, {}), jsxRuntimeExports.jsx("span", { children: isLoading ? "Logging in..." : "Sign in with AD" })] })] }), jsxRuntimeExports.jsxs(CardFooter, { className: "flex items-center justify-center mt-6 space-x-2 text-gray-400 text-sm", children: [jsxRuntimeExports.jsx(lucideReact.ShieldCheck, { className: "w-4 h-4 text-[#506f4a] pulse-glow" }), jsxRuntimeExports.jsx("span", { children: "Powered By NIBSS" })] })] }) }) }, "landing"), jsxRuntimeExports.jsx(ADLoginModal, { open: showADModal, onOpenChange: setShowADModal, onLogin: function (username, password, mfaCode) { return __awaiter(void 0, void 0, void 0, function () {
|
|
1993
|
+
return __generator(this, function (_a) {
|
|
1994
|
+
// Implement your AD login logic here
|
|
1995
|
+
console.log("AD Login:", { username: username, password: password, mfaCode: mfaCode });
|
|
1996
|
+
return [2 /*return*/];
|
|
1997
|
+
});
|
|
1998
|
+
}); } })] }));
|
|
1891
1999
|
};
|
|
1892
2000
|
|
|
1893
2001
|
var ErrorFallback = function (_a) {
|