@djangocfg/api 2.1.423 → 2.1.424
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/auth.cjs +3 -15
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +0 -7
- package/dist/auth.d.ts +0 -7
- package/dist/auth.mjs +3 -15
- package/dist/auth.mjs.map +1 -1
- package/package.json +2 -2
- package/src/auth/__tests__/useAuthForm.2fa.test.ts +10 -84
- package/src/auth/hooks/useAuthForm.ts +5 -18
- package/src/auth/types/form.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/api",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.424",
|
|
4
4
|
"description": "Auto-generated TypeScript API client with React hooks, SWR integration, and Zod validation for Django REST Framework backends",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "^25.2.3",
|
|
86
86
|
"@types/react": "^19.2.15",
|
|
87
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
87
|
+
"@djangocfg/typescript-config": "^2.1.424",
|
|
88
88
|
"next": "^16.2.2",
|
|
89
89
|
"react": "^19.2.4",
|
|
90
90
|
"tsup": "^8.5.0",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for useAuthForm 2FA
|
|
2
|
+
* Tests for useAuthForm 2FA behavior.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* the 2FA
|
|
4
|
+
* The sign-in flow only *verifies* an existing 2FA challenge — it never
|
|
5
|
+
* prompts the user to *set up* 2FA (that lives in ProfileLayout). These tests
|
|
6
|
+
* cover the step machine and the shouldPrompt2FA state that the verification
|
|
7
|
+
* branch relies on.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { act, renderHook } from '@testing-library/react';
|
|
@@ -40,7 +42,7 @@ jest.mock('../utils/logger', () => ({
|
|
|
40
42
|
},
|
|
41
43
|
}));
|
|
42
44
|
|
|
43
|
-
describe('useAuthForm - 2FA
|
|
45
|
+
describe('useAuthForm - 2FA', () => {
|
|
44
46
|
const defaultOptions = {
|
|
45
47
|
sourceUrl: 'http://localhost',
|
|
46
48
|
redirectUrl: '/dashboard',
|
|
@@ -50,96 +52,20 @@ describe('useAuthForm - 2FA Setup', () => {
|
|
|
50
52
|
jest.clearAllMocks();
|
|
51
53
|
});
|
|
52
54
|
|
|
53
|
-
describe('
|
|
54
|
-
it('should
|
|
55
|
+
describe('step transitions', () => {
|
|
56
|
+
it('should start on the identifier step', () => {
|
|
55
57
|
const { result } = renderHook(() => useAuthForm(defaultOptions));
|
|
56
58
|
|
|
57
|
-
// The hook should be initialized with default enable2FASetup=true
|
|
58
|
-
// We can verify this by checking that the hook is ready
|
|
59
|
-
expect(result.current.step).toBe('identifier');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should accept enable2FASetup=false', () => {
|
|
63
|
-
const { result } = renderHook(() =>
|
|
64
|
-
useAuthForm({
|
|
65
|
-
...defaultOptions,
|
|
66
|
-
enable2FASetup: false,
|
|
67
|
-
})
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
expect(result.current.step).toBe('identifier');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should accept enable2FASetup=true', () => {
|
|
74
|
-
const { result } = renderHook(() =>
|
|
75
|
-
useAuthForm({
|
|
76
|
-
...defaultOptions,
|
|
77
|
-
enable2FASetup: true,
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(result.current.step).toBe('identifier');
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('OTP verification with enable2FASetup', () => {
|
|
86
|
-
it('should go to 2fa-setup step when should_prompt_2fa=true and enable2FASetup=true', async () => {
|
|
87
|
-
// Mock verifyOTP to return should_prompt_2fa=true
|
|
88
|
-
const mockVerifyOTP = jest.fn().mockResolvedValue({
|
|
89
|
-
success: true,
|
|
90
|
-
should_prompt_2fa: true,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
jest.doMock('../context', () => ({
|
|
94
|
-
useAuth: () => ({
|
|
95
|
-
requestOTP: jest.fn().mockResolvedValue({ success: true }),
|
|
96
|
-
verifyOTP: mockVerifyOTP,
|
|
97
|
-
getSavedEmail: jest.fn().mockReturnValue('test@example.com'),
|
|
98
|
-
saveEmail: jest.fn(),
|
|
99
|
-
clearSavedEmail: jest.fn(),
|
|
100
|
-
getSavedPhone: jest.fn().mockReturnValue(null),
|
|
101
|
-
savePhone: jest.fn(),
|
|
102
|
-
clearSavedPhone: jest.fn(),
|
|
103
|
-
}),
|
|
104
|
-
}));
|
|
105
|
-
|
|
106
|
-
// Note: Due to module caching, this test demonstrates the expected behavior
|
|
107
|
-
// In a real test environment, you would need to properly mock the module
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should skip 2fa-setup step when should_prompt_2fa=true but enable2FASetup=false', async () => {
|
|
111
|
-
// This test verifies that when enable2FASetup is false,
|
|
112
|
-
// the user goes directly to success even if should_prompt_2fa is true
|
|
113
|
-
const { result } = renderHook(() =>
|
|
114
|
-
useAuthForm({
|
|
115
|
-
...defaultOptions,
|
|
116
|
-
enable2FASetup: false,
|
|
117
|
-
})
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
// Initial step should be identifier
|
|
121
59
|
expect(result.current.step).toBe('identifier');
|
|
122
60
|
});
|
|
123
|
-
});
|
|
124
61
|
|
|
125
|
-
|
|
126
|
-
it('should have correct step state handler', () => {
|
|
62
|
+
it('should expose a setStep handler', () => {
|
|
127
63
|
const { result } = renderHook(() => useAuthForm(defaultOptions));
|
|
128
64
|
|
|
129
65
|
expect(typeof result.current.setStep).toBe('function');
|
|
130
66
|
});
|
|
131
67
|
|
|
132
|
-
it('should be able to transition to
|
|
133
|
-
const { result } = renderHook(() => useAuthForm(defaultOptions));
|
|
134
|
-
|
|
135
|
-
act(() => {
|
|
136
|
-
result.current.setStep('2fa-setup');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
expect(result.current.step).toBe('2fa-setup');
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should be able to transition to success step', () => {
|
|
68
|
+
it('should be able to transition to the success step', () => {
|
|
143
69
|
const { result } = renderHook(() => useAuthForm(defaultOptions));
|
|
144
70
|
|
|
145
71
|
act(() => {
|
|
@@ -24,7 +24,6 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
|
|
|
24
24
|
redirectUrl,
|
|
25
25
|
requireTermsAcceptance = false,
|
|
26
26
|
authPath = '/auth',
|
|
27
|
-
enable2FASetup = true,
|
|
28
27
|
} = options;
|
|
29
28
|
|
|
30
29
|
// Compose smaller hooks
|
|
@@ -195,22 +194,10 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
|
|
|
195
194
|
if (result.success) {
|
|
196
195
|
saveIdentifierToStorage(submitIdentifier);
|
|
197
196
|
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
setShouldPrompt2FA(true);
|
|
203
|
-
setStep('2fa-setup');
|
|
204
|
-
onOTPSuccess?.();
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Skip 2FA setup prompt if enable2FASetup is false
|
|
209
|
-
if (result.should_prompt_2fa && !enable2FASetup) {
|
|
210
|
-
authLogger.info('OTP verification successful, skipping 2FA setup prompt (disabled)');
|
|
211
|
-
} else {
|
|
212
|
-
authLogger.info('OTP verification successful, showing success screen');
|
|
213
|
-
}
|
|
197
|
+
// Sign-in only verifies identity. Setting up 2FA is owned by
|
|
198
|
+
// ProfileLayout, so we never branch into a setup prompt here — the
|
|
199
|
+
// backend's `should_prompt_2fa` hint is intentionally ignored.
|
|
200
|
+
authLogger.info('OTP verification successful, showing success screen');
|
|
214
201
|
setStep('success');
|
|
215
202
|
onOTPSuccess?.();
|
|
216
203
|
return true;
|
|
@@ -227,7 +214,7 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
|
|
|
227
214
|
} finally {
|
|
228
215
|
setIsLoading(false);
|
|
229
216
|
}
|
|
230
|
-
}, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep
|
|
217
|
+
}, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
|
|
231
218
|
|
|
232
219
|
const handleOTPSubmit = useCallback(async (e: React.FormEvent) => {
|
|
233
220
|
e.preventDefault();
|
package/src/auth/types/form.ts
CHANGED
|
@@ -153,12 +153,5 @@ export interface UseAuthFormOptions {
|
|
|
153
153
|
requireTermsAcceptance?: boolean;
|
|
154
154
|
/** Path to auth page for auto-OTP detection. Default: '/auth' */
|
|
155
155
|
authPath?: string;
|
|
156
|
-
/**
|
|
157
|
-
* Enable 2FA setup prompt after successful authentication.
|
|
158
|
-
* When true (default), users without 2FA will see a setup prompt after login.
|
|
159
|
-
* When false, users go directly to success without 2FA setup prompt.
|
|
160
|
-
* @default true
|
|
161
|
-
*/
|
|
162
|
-
enable2FASetup?: boolean;
|
|
163
156
|
}
|
|
164
157
|
|