@pubflow/react 0.4.3 → 0.4.5
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/index.cjs +341 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +342 -17
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -206,7 +206,8 @@ class BrowserStorageAdapter {
|
|
|
206
206
|
*/
|
|
207
207
|
const PubflowContext = React.createContext({
|
|
208
208
|
instances: {},
|
|
209
|
-
defaultInstance: 'default'
|
|
209
|
+
defaultInstance: 'default',
|
|
210
|
+
isReady: false
|
|
210
211
|
});
|
|
211
212
|
/**
|
|
212
213
|
* Pubflow Provider Component
|
|
@@ -214,7 +215,8 @@ const PubflowContext = React.createContext({
|
|
|
214
215
|
function PubflowProvider({ children, config, instances, defaultInstance = 'default', enableDebugTools = false, showSessionAlerts = false, persistentCache = { enabled: false }, theme = {}, loginRedirectPath = '/login', publicPaths = [] }) {
|
|
215
216
|
const [contextValue, setContextValue] = React.useState({
|
|
216
217
|
instances: {},
|
|
217
|
-
defaultInstance
|
|
218
|
+
defaultInstance,
|
|
219
|
+
isReady: false
|
|
218
220
|
});
|
|
219
221
|
// Session handlers
|
|
220
222
|
const handleSessionExpired = React.useCallback(() => {
|
|
@@ -252,6 +254,7 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
252
254
|
});
|
|
253
255
|
const apiClient = new core.ApiClient(fullConfig, storage);
|
|
254
256
|
const authService = new core.AuthService(apiClient, storage, fullConfig);
|
|
257
|
+
const twoFactorService = new core.TwoFactorService(apiClient, fullConfig);
|
|
255
258
|
// Get current user (non-blocking)
|
|
256
259
|
let user = null;
|
|
257
260
|
let isAuthenticated = false;
|
|
@@ -259,12 +262,35 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
259
262
|
config: fullConfig,
|
|
260
263
|
apiClient,
|
|
261
264
|
authService,
|
|
265
|
+
twoFactorService,
|
|
262
266
|
user,
|
|
263
267
|
isAuthenticated,
|
|
264
268
|
isLoading: true,
|
|
269
|
+
twoFactorPending: false,
|
|
270
|
+
twoFactorMethods: [],
|
|
265
271
|
login: async (credentials) => {
|
|
266
272
|
const result = await authService.login(credentials);
|
|
267
|
-
if (
|
|
273
|
+
if (!isMounted)
|
|
274
|
+
return result;
|
|
275
|
+
if (result.success && result.requires2fa) {
|
|
276
|
+
// 2FA required — keep session pending, expose methods
|
|
277
|
+
setContextValue(prev => {
|
|
278
|
+
var _a;
|
|
279
|
+
return ({
|
|
280
|
+
...prev,
|
|
281
|
+
instances: {
|
|
282
|
+
...prev.instances,
|
|
283
|
+
[instanceConfig.id]: {
|
|
284
|
+
...prev.instances[instanceConfig.id],
|
|
285
|
+
isLoading: false,
|
|
286
|
+
twoFactorPending: true,
|
|
287
|
+
twoFactorMethods: (_a = result.availableMethods) !== null && _a !== void 0 ? _a : [],
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
else if (result.success && result.user) {
|
|
268
294
|
setContextValue(prev => ({
|
|
269
295
|
...prev,
|
|
270
296
|
instances: {
|
|
@@ -273,7 +299,9 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
273
299
|
...prev.instances[instanceConfig.id],
|
|
274
300
|
user: result.user,
|
|
275
301
|
isAuthenticated: true,
|
|
276
|
-
isLoading: false
|
|
302
|
+
isLoading: false,
|
|
303
|
+
twoFactorPending: false,
|
|
304
|
+
twoFactorMethods: [],
|
|
277
305
|
}
|
|
278
306
|
}
|
|
279
307
|
}));
|
|
@@ -291,7 +319,9 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
291
319
|
...prev.instances[instanceConfig.id],
|
|
292
320
|
user: null,
|
|
293
321
|
isAuthenticated: false,
|
|
294
|
-
isLoading: false
|
|
322
|
+
isLoading: false,
|
|
323
|
+
twoFactorPending: false,
|
|
324
|
+
twoFactorMethods: [],
|
|
295
325
|
}
|
|
296
326
|
}
|
|
297
327
|
}));
|
|
@@ -314,7 +344,31 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
314
344
|
}));
|
|
315
345
|
}
|
|
316
346
|
return result;
|
|
317
|
-
}
|
|
347
|
+
},
|
|
348
|
+
verifyTwoFactor: async (methodId, code) => {
|
|
349
|
+
const result = await twoFactorService.verify(methodId, code, 'login');
|
|
350
|
+
if (result.verified && isMounted) {
|
|
351
|
+
// Fetch the now-active user from the server
|
|
352
|
+
const userData = await authService.getCurrentUser();
|
|
353
|
+
setContextValue(prev => ({
|
|
354
|
+
...prev,
|
|
355
|
+
instances: {
|
|
356
|
+
...prev.instances,
|
|
357
|
+
[instanceConfig.id]: {
|
|
358
|
+
...prev.instances[instanceConfig.id],
|
|
359
|
+
user: userData,
|
|
360
|
+
isAuthenticated: true,
|
|
361
|
+
twoFactorPending: false,
|
|
362
|
+
twoFactorMethods: [],
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
},
|
|
369
|
+
startTwoFactor: async (methodId, method) => {
|
|
370
|
+
return twoFactorService.start(methodId, method, 'login');
|
|
371
|
+
},
|
|
318
372
|
};
|
|
319
373
|
// Load user asynchronously
|
|
320
374
|
authService.getCurrentUser().then(currentUser => {
|
|
@@ -363,16 +417,39 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
363
417
|
});
|
|
364
418
|
const apiClient = new core.ApiClient(fullConfig, storage);
|
|
365
419
|
const authService = new core.AuthService(apiClient, storage, fullConfig);
|
|
420
|
+
const twoFactorService = new core.TwoFactorService(apiClient, fullConfig);
|
|
366
421
|
instancesMap[defaultInstance] = {
|
|
367
422
|
config: fullConfig,
|
|
368
423
|
apiClient,
|
|
369
424
|
authService,
|
|
425
|
+
twoFactorService,
|
|
370
426
|
user: null,
|
|
371
427
|
isAuthenticated: false,
|
|
372
428
|
isLoading: true,
|
|
429
|
+
twoFactorPending: false,
|
|
430
|
+
twoFactorMethods: [],
|
|
373
431
|
login: async (credentials) => {
|
|
374
432
|
const result = await authService.login(credentials);
|
|
375
|
-
if (
|
|
433
|
+
if (!isMounted)
|
|
434
|
+
return result;
|
|
435
|
+
if (result.success && result.requires2fa) {
|
|
436
|
+
setContextValue(prev => {
|
|
437
|
+
var _a;
|
|
438
|
+
return ({
|
|
439
|
+
...prev,
|
|
440
|
+
instances: {
|
|
441
|
+
...prev.instances,
|
|
442
|
+
[defaultInstance]: {
|
|
443
|
+
...prev.instances[defaultInstance],
|
|
444
|
+
isLoading: false,
|
|
445
|
+
twoFactorPending: true,
|
|
446
|
+
twoFactorMethods: (_a = result.availableMethods) !== null && _a !== void 0 ? _a : [],
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
else if (result.success && result.user) {
|
|
376
453
|
setContextValue(prev => ({
|
|
377
454
|
...prev,
|
|
378
455
|
instances: {
|
|
@@ -381,7 +458,9 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
381
458
|
...prev.instances[defaultInstance],
|
|
382
459
|
user: result.user,
|
|
383
460
|
isAuthenticated: true,
|
|
384
|
-
isLoading: false
|
|
461
|
+
isLoading: false,
|
|
462
|
+
twoFactorPending: false,
|
|
463
|
+
twoFactorMethods: [],
|
|
385
464
|
}
|
|
386
465
|
}
|
|
387
466
|
}));
|
|
@@ -399,7 +478,9 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
399
478
|
...prev.instances[defaultInstance],
|
|
400
479
|
user: null,
|
|
401
480
|
isAuthenticated: false,
|
|
402
|
-
isLoading: false
|
|
481
|
+
isLoading: false,
|
|
482
|
+
twoFactorPending: false,
|
|
483
|
+
twoFactorMethods: [],
|
|
403
484
|
}
|
|
404
485
|
}
|
|
405
486
|
}));
|
|
@@ -422,7 +503,30 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
422
503
|
}));
|
|
423
504
|
}
|
|
424
505
|
return result;
|
|
425
|
-
}
|
|
506
|
+
},
|
|
507
|
+
verifyTwoFactor: async (methodId, code) => {
|
|
508
|
+
const result = await twoFactorService.verify(methodId, code, 'login');
|
|
509
|
+
if (result.verified && isMounted) {
|
|
510
|
+
const userData = await authService.getCurrentUser();
|
|
511
|
+
setContextValue(prev => ({
|
|
512
|
+
...prev,
|
|
513
|
+
instances: {
|
|
514
|
+
...prev.instances,
|
|
515
|
+
[defaultInstance]: {
|
|
516
|
+
...prev.instances[defaultInstance],
|
|
517
|
+
user: userData,
|
|
518
|
+
isAuthenticated: true,
|
|
519
|
+
twoFactorPending: false,
|
|
520
|
+
twoFactorMethods: [],
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}));
|
|
524
|
+
}
|
|
525
|
+
return result;
|
|
526
|
+
},
|
|
527
|
+
startTwoFactor: async (methodId, method) => {
|
|
528
|
+
return twoFactorService.start(methodId, method, 'login');
|
|
529
|
+
},
|
|
426
530
|
};
|
|
427
531
|
// Load user asynchronously
|
|
428
532
|
authService.getCurrentUser().then(currentUser => {
|
|
@@ -459,9 +563,14 @@ function PubflowProvider({ children, config, instances, defaultInstance = 'defau
|
|
|
459
563
|
if (isMounted && Object.keys(instancesMap).length > 0) {
|
|
460
564
|
setContextValue({
|
|
461
565
|
instances: instancesMap,
|
|
462
|
-
defaultInstance
|
|
566
|
+
defaultInstance,
|
|
567
|
+
isReady: true
|
|
463
568
|
});
|
|
464
569
|
}
|
|
570
|
+
else if (isMounted) {
|
|
571
|
+
// Even if no instances configured correctly, mark ready so useAuth can react
|
|
572
|
+
setContextValue(prev => ({ ...prev, isReady: true }));
|
|
573
|
+
}
|
|
465
574
|
}
|
|
466
575
|
catch (error) {
|
|
467
576
|
console.error('Error initializing Pubflow:', error);
|
|
@@ -512,6 +621,23 @@ function useAuth(instanceId) {
|
|
|
512
621
|
}
|
|
513
622
|
const instance = instanceId || context.defaultInstance;
|
|
514
623
|
const pubflowInstance = context.instances[instance];
|
|
624
|
+
// If the provider hasn't finished its initial useEffect setup,
|
|
625
|
+
// instances won't exist yet. Return a loading state instead of crashing.
|
|
626
|
+
if (!context.isReady) {
|
|
627
|
+
return {
|
|
628
|
+
user: null,
|
|
629
|
+
isAuthenticated: false,
|
|
630
|
+
isLoading: true, // Crucial: tell the UI we are still loading the provider
|
|
631
|
+
twoFactorPending: false,
|
|
632
|
+
twoFactorMethods: [],
|
|
633
|
+
login: async () => { throw new Error('Pubflow not initialized yet'); },
|
|
634
|
+
logout: async () => { throw new Error('Pubflow not initialized yet'); },
|
|
635
|
+
validateSession: async () => ({ isValid: false }),
|
|
636
|
+
verifyTwoFactor: async () => ({ success: false, error: 'Pubflow not initialized yet' }),
|
|
637
|
+
startTwoFactor: async () => ({ success: false, error: 'Pubflow not initialized yet' }),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
// After provider is ready, if the instance is STILL missing, it's a real config error.
|
|
515
641
|
if (!pubflowInstance) {
|
|
516
642
|
throw new Error(`Pubflow instance '${instance}' not found`);
|
|
517
643
|
}
|
|
@@ -520,9 +646,13 @@ function useAuth(instanceId) {
|
|
|
520
646
|
user: pubflowInstance.user || null,
|
|
521
647
|
isAuthenticated: pubflowInstance.isAuthenticated,
|
|
522
648
|
isLoading: pubflowInstance.isLoading,
|
|
649
|
+
twoFactorPending: pubflowInstance.twoFactorPending,
|
|
650
|
+
twoFactorMethods: pubflowInstance.twoFactorMethods,
|
|
523
651
|
login: pubflowInstance.login,
|
|
524
652
|
logout: pubflowInstance.logout,
|
|
525
|
-
validateSession: pubflowInstance.validateSession
|
|
653
|
+
validateSession: pubflowInstance.validateSession,
|
|
654
|
+
verifyTwoFactor: pubflowInstance.verifyTwoFactor,
|
|
655
|
+
startTwoFactor: pubflowInstance.startTwoFactor,
|
|
526
656
|
};
|
|
527
657
|
}
|
|
528
658
|
|
|
@@ -8627,17 +8757,25 @@ function processLogoUrl(logoUrl) {
|
|
|
8627
8757
|
* Professional login form component
|
|
8628
8758
|
*/
|
|
8629
8759
|
function LoginForm({ config = {}, onSuccess, onError, onPasswordReset, onAccountCreation, instanceId }) {
|
|
8630
|
-
const { login, isLoading } = useAuth(instanceId);
|
|
8760
|
+
const { login, verifyTwoFactor, startTwoFactor, isLoading } = useAuth(instanceId);
|
|
8631
8761
|
// Form state
|
|
8632
8762
|
const [email, setEmail] = React.useState('');
|
|
8633
8763
|
const [password, setPassword] = React.useState('');
|
|
8634
8764
|
const [showPassword, setShowPassword] = React.useState(false);
|
|
8635
8765
|
const [error, setError] = React.useState('');
|
|
8636
8766
|
const [rememberMe, setRememberMe] = React.useState(false);
|
|
8767
|
+
// 2FA state
|
|
8768
|
+
const [step, setStep] = React.useState('credentials');
|
|
8769
|
+
const [otpCode, setOtpCode] = React.useState('');
|
|
8770
|
+
const [pendingMethods, setPendingMethods] = React.useState([]);
|
|
8771
|
+
const [activeMethodId, setActiveMethodId] = React.useState(null);
|
|
8772
|
+
const [activeMethodName, setActiveMethodName] = React.useState('email');
|
|
8773
|
+
const [otpLoading, setOtpLoading] = React.useState(false);
|
|
8637
8774
|
// Configuration with defaults
|
|
8638
8775
|
const { primaryColor = process.env.REACT_APP_PRIMARY_COLOR || '#006aff', secondaryColor = process.env.REACT_APP_SECONDARY_COLOR || '#4a90e2', appName = process.env.REACT_APP_NAME || 'Pubflow', logo = process.env.REACT_APP_LOGO, showPasswordReset = true, showAccountCreation = true, className = '', redirectPath } = config;
|
|
8639
8776
|
// Handle form submission
|
|
8640
8777
|
const handleSubmit = async (e) => {
|
|
8778
|
+
var _a;
|
|
8641
8779
|
e.preventDefault();
|
|
8642
8780
|
setError('');
|
|
8643
8781
|
// Basic validation
|
|
@@ -8651,6 +8789,17 @@ function LoginForm({ config = {}, onSuccess, onError, onPasswordReset, onAccount
|
|
|
8651
8789
|
}
|
|
8652
8790
|
try {
|
|
8653
8791
|
const result = await login({ email: email.toLowerCase().trim(), password });
|
|
8792
|
+
if (result.requires2fa) {
|
|
8793
|
+
// Switch to OTP step
|
|
8794
|
+
const methods = (_a = result.availableMethods) !== null && _a !== void 0 ? _a : [];
|
|
8795
|
+
setPendingMethods(methods);
|
|
8796
|
+
if (methods.length > 0) {
|
|
8797
|
+
setActiveMethodId(methods[0].id);
|
|
8798
|
+
setActiveMethodName(methods[0].method);
|
|
8799
|
+
}
|
|
8800
|
+
setStep('otp');
|
|
8801
|
+
return;
|
|
8802
|
+
}
|
|
8654
8803
|
if (result.success) {
|
|
8655
8804
|
if (onSuccess) {
|
|
8656
8805
|
onSuccess(result.user);
|
|
@@ -8676,6 +8825,56 @@ function LoginForm({ config = {}, onSuccess, onError, onPasswordReset, onAccount
|
|
|
8676
8825
|
}
|
|
8677
8826
|
}
|
|
8678
8827
|
};
|
|
8828
|
+
// Handle OTP verification
|
|
8829
|
+
const handleOtpSubmit = async (e) => {
|
|
8830
|
+
e.preventDefault();
|
|
8831
|
+
if (!activeMethodId)
|
|
8832
|
+
return;
|
|
8833
|
+
setError('');
|
|
8834
|
+
setOtpLoading(true);
|
|
8835
|
+
try {
|
|
8836
|
+
const result = await verifyTwoFactor(activeMethodId, otpCode.trim());
|
|
8837
|
+
if (result.verified) {
|
|
8838
|
+
if (onSuccess)
|
|
8839
|
+
onSuccess(null);
|
|
8840
|
+
if (redirectPath && typeof window !== 'undefined') {
|
|
8841
|
+
window.location.href = redirectPath;
|
|
8842
|
+
}
|
|
8843
|
+
}
|
|
8844
|
+
else {
|
|
8845
|
+
const msg = result.error ||
|
|
8846
|
+
(result.attempts_remaining !== undefined
|
|
8847
|
+
? `Incorrect code. ${result.attempts_remaining} attempt(s) remaining.`
|
|
8848
|
+
: 'Incorrect code.');
|
|
8849
|
+
setError(msg);
|
|
8850
|
+
if (onError)
|
|
8851
|
+
onError(msg);
|
|
8852
|
+
}
|
|
8853
|
+
}
|
|
8854
|
+
catch (err) {
|
|
8855
|
+
const msg = err instanceof Error ? err.message : 'Verification failed';
|
|
8856
|
+
setError(msg);
|
|
8857
|
+
if (onError)
|
|
8858
|
+
onError(msg);
|
|
8859
|
+
}
|
|
8860
|
+
finally {
|
|
8861
|
+
setOtpLoading(false);
|
|
8862
|
+
}
|
|
8863
|
+
};
|
|
8864
|
+
// Handle resend code
|
|
8865
|
+
const handleResend = async () => {
|
|
8866
|
+
if (!activeMethodId)
|
|
8867
|
+
return;
|
|
8868
|
+
setError('');
|
|
8869
|
+
setOtpLoading(true);
|
|
8870
|
+
try {
|
|
8871
|
+
await startTwoFactor(activeMethodId, activeMethodName);
|
|
8872
|
+
}
|
|
8873
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
8874
|
+
finally {
|
|
8875
|
+
setOtpLoading(false);
|
|
8876
|
+
}
|
|
8877
|
+
};
|
|
8679
8878
|
// Dynamic styles
|
|
8680
8879
|
const styles = {
|
|
8681
8880
|
container: {
|
|
@@ -8691,6 +8890,7 @@ function LoginForm({ config = {}, onSuccess, onError, onPasswordReset, onAccount
|
|
|
8691
8890
|
},
|
|
8692
8891
|
logoSection: {
|
|
8693
8892
|
display: 'flex',
|
|
8893
|
+
flexDirection: 'column',
|
|
8694
8894
|
justifyContent: 'center',
|
|
8695
8895
|
alignItems: 'center',
|
|
8696
8896
|
textAlign: 'center',
|
|
@@ -8813,11 +9013,17 @@ function LoginForm({ config = {}, onSuccess, onError, onPasswordReset, onAccount
|
|
|
8813
9013
|
textAlign: 'center'
|
|
8814
9014
|
}
|
|
8815
9015
|
};
|
|
8816
|
-
return (jsxRuntime.jsxs("div", { className: className, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.logoSection, children: [logo && (typeof logo === 'string' ? (jsxRuntime.jsx("img", { src: processLogoUrl(logo), alt: `${appName} Logo`, style: styles.logo })) : (logo)), jsxRuntime.jsxs("h1", { style: styles.welcomeText, children: ["Welcome to ", appName] }), jsxRuntime.jsx("p", { style: styles.subtitleText, children:
|
|
9016
|
+
return (jsxRuntime.jsxs("div", { className: className, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.logoSection, children: [logo && (typeof logo === 'string' ? (jsxRuntime.jsx("img", { src: processLogoUrl(logo), alt: `${appName} Logo`, style: styles.logo })) : (logo)), jsxRuntime.jsxs("h1", { style: styles.welcomeText, children: ["Welcome to ", appName] }), jsxRuntime.jsx("p", { style: styles.subtitleText, children: step === 'otp' ? 'Enter the verification code we sent you.' : 'Sign in to your account' })] }), error && (jsxRuntime.jsxs("div", { style: styles.errorContainer, children: [jsxRuntime.jsx("span", { style: { color: '#ff4757', fontSize: '20px' }, children: "\u26A0" }), jsxRuntime.jsx("span", { style: styles.errorText, children: error })] })), step === 'otp' ? (jsxRuntime.jsxs("form", { onSubmit: handleOtpSubmit, children: [jsxRuntime.jsxs("div", { style: styles.inputGroup, children: [jsxRuntime.jsx("label", { style: styles.inputLabel, children: "Verification Code" }), jsxRuntime.jsxs("div", { style: styles.inputContainer, children: [jsxRuntime.jsx("span", { style: { marginRight: '12px', opacity: 0.7, color: '#666' }, children: "\uD83D\uDD10" }), jsxRuntime.jsx("input", { type: "text", inputMode: "numeric", pattern: "[0-9]*", maxLength: 6, style: { ...styles.input, letterSpacing: '0.4em', textAlign: 'center' }, placeholder: "000000", value: otpCode, onChange: (e) => setOtpCode(e.target.value.replace(/\D/g, '')), autoFocus: true, autoComplete: "one-time-code", disabled: otpLoading, required: true })] })] }), jsxRuntime.jsx("button", { type: "submit", style: {
|
|
9017
|
+
...styles.loginButton,
|
|
9018
|
+
opacity: otpLoading || otpCode.length < 6 ? 0.7 : 1,
|
|
9019
|
+
cursor: otpLoading || otpCode.length < 6 ? 'not-allowed' : 'pointer',
|
|
9020
|
+
}, disabled: otpLoading || otpCode.length < 6, children: otpLoading ? '⟳ Verifying…' : 'Verify Code' }), jsxRuntime.jsxs("div", { style: { textAlign: 'center', marginTop: '16px' }, children: [jsxRuntime.jsx("button", { type: "button", style: { ...styles.linkButton, fontSize: '14px' }, onClick: handleResend, disabled: otpLoading, children: "Didn't receive a code? Resend" }), jsxRuntime.jsx("button", { type: "button", style: { ...styles.linkButton, fontSize: '13px', color: '#888' }, onClick: () => { setStep('credentials'); setError(''); setOtpCode(''); }, children: "\u2190 Back to login" })] })] })) : (
|
|
9021
|
+
/* ── Credentials Step ── */
|
|
9022
|
+
jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [jsxRuntime.jsxs("div", { style: styles.inputGroup, children: [jsxRuntime.jsx("label", { style: styles.inputLabel, children: "Email Address" }), jsxRuntime.jsxs("div", { style: styles.inputContainer, children: [jsxRuntime.jsx("span", { style: { marginRight: '12px', opacity: 0.7, color: '#666' }, children: "\u2709" }), jsxRuntime.jsx("input", { type: "email", style: styles.input, placeholder: "Enter your email", value: email, onChange: (e) => setEmail(e.target.value), autoCapitalize: "none", autoComplete: "email", disabled: isLoading, required: true })] })] }), jsxRuntime.jsxs("div", { style: styles.inputGroup, children: [jsxRuntime.jsx("label", { style: styles.inputLabel, children: "Password" }), jsxRuntime.jsxs("div", { style: styles.inputContainer, children: [jsxRuntime.jsx("span", { style: { marginRight: '12px', opacity: 0.7, color: '#666' }, children: "\uD83D\uDD12" }), jsxRuntime.jsx("input", { type: showPassword ? 'text' : 'password', style: styles.input, placeholder: "Enter your password", value: password, onChange: (e) => setPassword(e.target.value), autoComplete: "current-password", disabled: isLoading, required: true }), jsxRuntime.jsx("button", { type: "button", style: styles.eyeButton, onClick: () => setShowPassword(!showPassword), disabled: isLoading, children: showPassword ? '👁' : '👁🗨' })] })] }), jsxRuntime.jsxs("div", { style: styles.checkboxContainer, children: [jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', fontSize: '14px', color: '#666' }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: rememberMe, onChange: (e) => setRememberMe(e.target.checked), style: { marginRight: '8px' } }), "Remember me"] }), showPasswordReset && (jsxRuntime.jsx("button", { type: "button", style: { ...styles.linkButton, fontSize: '14px' }, onClick: onPasswordReset, disabled: isLoading, children: "Forgot password?" }))] }), jsxRuntime.jsx("button", { type: "submit", style: {
|
|
8817
9023
|
...styles.loginButton,
|
|
8818
9024
|
opacity: isLoading ? 0.8 : 1,
|
|
8819
9025
|
cursor: isLoading ? 'not-allowed' : 'pointer'
|
|
8820
|
-
}, disabled: isLoading, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { animation: 'spin 1s linear infinite' }, children: "\u27F3" }), "Signing in..."] })) : ('Sign In') })
|
|
9026
|
+
}, disabled: isLoading, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { animation: 'spin 1s linear infinite' }, children: "\u27F3" }), "Signing in..."] })) : ('Sign In') }), showAccountCreation && (jsxRuntime.jsx("div", { style: styles.accountActions, children: jsxRuntime.jsx("button", { type: "button", style: styles.linkButton, onClick: onAccountCreation, disabled: isLoading, children: "Create New Account" }) }))] })), jsxRuntime.jsx("style", { children: `
|
|
8821
9027
|
@keyframes spin {
|
|
8822
9028
|
from { transform: rotate(0deg); }
|
|
8823
9029
|
to { transform: rotate(360deg); }
|
|
@@ -9830,6 +10036,125 @@ function useBridgeApiRaw(instanceId) {
|
|
|
9830
10036
|
};
|
|
9831
10037
|
}
|
|
9832
10038
|
|
|
10039
|
+
/**
|
|
10040
|
+
* useTwoFactor Hook for React
|
|
10041
|
+
*
|
|
10042
|
+
* Convenience hook for managing 2FA settings (setup, toggle, remove).
|
|
10043
|
+
* For the LOGIN-TIME 2FA flow use useAuth() which surfaces
|
|
10044
|
+
* twoFactorPending / verifyTwoFactor / startTwoFactor.
|
|
10045
|
+
*/
|
|
10046
|
+
/**
|
|
10047
|
+
* Hook for managing 2FA settings.
|
|
10048
|
+
*
|
|
10049
|
+
* Must be used inside a PubflowProvider.
|
|
10050
|
+
*
|
|
10051
|
+
* @param instanceId Optional Pubflow instance ID
|
|
10052
|
+
*/
|
|
10053
|
+
function useTwoFactor(instanceId) {
|
|
10054
|
+
const context = React.useContext(PubflowContext);
|
|
10055
|
+
if (!context)
|
|
10056
|
+
throw new Error('useTwoFactor must be used within a PubflowProvider');
|
|
10057
|
+
const id = instanceId || context.defaultInstance;
|
|
10058
|
+
const instance = context.instances[id];
|
|
10059
|
+
const [systemEnabled, setSystemEnabled] = React.useState(false);
|
|
10060
|
+
const [availableMethods, setAvailableMethods] = React.useState([]);
|
|
10061
|
+
const [methods, setMethods] = React.useState([]);
|
|
10062
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
10063
|
+
const [error, setError] = React.useState(null);
|
|
10064
|
+
const refresh = React.useCallback(async () => {
|
|
10065
|
+
if (!instance)
|
|
10066
|
+
return;
|
|
10067
|
+
setIsLoading(true);
|
|
10068
|
+
setError(null);
|
|
10069
|
+
try {
|
|
10070
|
+
const [sys, userMethods] = await Promise.all([
|
|
10071
|
+
instance.twoFactorService.getSystem(),
|
|
10072
|
+
instance.twoFactorService.getMethods(),
|
|
10073
|
+
]);
|
|
10074
|
+
setSystemEnabled(sys.global_two_factor_enabled);
|
|
10075
|
+
setAvailableMethods(sys.available_methods);
|
|
10076
|
+
setMethods(userMethods);
|
|
10077
|
+
}
|
|
10078
|
+
catch (e) {
|
|
10079
|
+
setError((e === null || e === void 0 ? void 0 : e.message) || 'Failed to load 2FA info');
|
|
10080
|
+
}
|
|
10081
|
+
finally {
|
|
10082
|
+
setIsLoading(false);
|
|
10083
|
+
}
|
|
10084
|
+
}, [instance]);
|
|
10085
|
+
const setup = React.useCallback(async (method, identifier) => {
|
|
10086
|
+
if (!instance)
|
|
10087
|
+
return { success: false, error: 'Pubflow not initialized' };
|
|
10088
|
+
setIsLoading(true);
|
|
10089
|
+
setError(null);
|
|
10090
|
+
try {
|
|
10091
|
+
const result = await instance.twoFactorService.setup(method, identifier);
|
|
10092
|
+
if (result.success)
|
|
10093
|
+
await refresh();
|
|
10094
|
+
return result;
|
|
10095
|
+
}
|
|
10096
|
+
catch (e) {
|
|
10097
|
+
const msg = (e === null || e === void 0 ? void 0 : e.message) || 'Setup failed';
|
|
10098
|
+
setError(msg);
|
|
10099
|
+
return { success: false, error: msg };
|
|
10100
|
+
}
|
|
10101
|
+
finally {
|
|
10102
|
+
setIsLoading(false);
|
|
10103
|
+
}
|
|
10104
|
+
}, [instance, refresh]);
|
|
10105
|
+
const toggle = React.useCallback(async (enabled, verificationCode, verificationMethodId) => {
|
|
10106
|
+
if (!instance)
|
|
10107
|
+
return { success: false, error: 'Pubflow not initialized' };
|
|
10108
|
+
setIsLoading(true);
|
|
10109
|
+
setError(null);
|
|
10110
|
+
try {
|
|
10111
|
+
const result = await instance.twoFactorService.toggle(enabled, verificationCode, verificationMethodId);
|
|
10112
|
+
if (result.success)
|
|
10113
|
+
await refresh();
|
|
10114
|
+
return result;
|
|
10115
|
+
}
|
|
10116
|
+
catch (e) {
|
|
10117
|
+
const msg = (e === null || e === void 0 ? void 0 : e.message) || 'Toggle failed';
|
|
10118
|
+
setError(msg);
|
|
10119
|
+
return { success: false, error: msg };
|
|
10120
|
+
}
|
|
10121
|
+
finally {
|
|
10122
|
+
setIsLoading(false);
|
|
10123
|
+
}
|
|
10124
|
+
}, [instance, refresh]);
|
|
10125
|
+
const removeMethod = React.useCallback(async (methodId, verificationCode, verificationMethodId) => {
|
|
10126
|
+
if (!instance)
|
|
10127
|
+
return { success: false, error: 'Pubflow not initialized' };
|
|
10128
|
+
setIsLoading(true);
|
|
10129
|
+
setError(null);
|
|
10130
|
+
try {
|
|
10131
|
+
const result = await instance.twoFactorService.removeMethod(methodId, verificationCode, verificationMethodId);
|
|
10132
|
+
if (result.success)
|
|
10133
|
+
await refresh();
|
|
10134
|
+
return result;
|
|
10135
|
+
}
|
|
10136
|
+
catch (e) {
|
|
10137
|
+
const msg = (e === null || e === void 0 ? void 0 : e.message) || 'Remove failed';
|
|
10138
|
+
setError(msg);
|
|
10139
|
+
return { success: false, error: msg };
|
|
10140
|
+
}
|
|
10141
|
+
finally {
|
|
10142
|
+
setIsLoading(false);
|
|
10143
|
+
}
|
|
10144
|
+
}, [instance, refresh]);
|
|
10145
|
+
return {
|
|
10146
|
+
systemEnabled,
|
|
10147
|
+
availableMethods,
|
|
10148
|
+
methods,
|
|
10149
|
+
isLoading,
|
|
10150
|
+
error,
|
|
10151
|
+
refresh,
|
|
10152
|
+
setup,
|
|
10153
|
+
toggle,
|
|
10154
|
+
removeMethod,
|
|
10155
|
+
};
|
|
10156
|
+
}
|
|
10157
|
+
|
|
9833
10158
|
/**
|
|
9834
10159
|
* Search Query Builder Hook for React
|
|
9835
10160
|
*
|
|
@@ -10082,6 +10407,7 @@ exports.useRequireAuth = useRequireAuth;
|
|
|
10082
10407
|
exports.useSearchQueryBuilder = useSearchQueryBuilder;
|
|
10083
10408
|
exports.useSimpleAuthGuard = useSimpleAuthGuard;
|
|
10084
10409
|
exports.useTheme = useTheme;
|
|
10410
|
+
exports.useTwoFactor = useTwoFactor;
|
|
10085
10411
|
Object.keys(core).forEach(function (k) {
|
|
10086
10412
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
10087
10413
|
enumerable: true,
|