@djangocfg/api 2.1.57 → 2.1.59
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 +125 -9
- package/dist/auth.cjs +1865 -402
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +352 -76
- package/dist/auth.d.ts +352 -76
- package/dist/auth.mjs +1867 -404
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +1637 -137
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +1394 -282
- package/dist/clients.d.ts +1394 -282
- package/dist/clients.mjs +1637 -137
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +24 -11
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +88 -21
- package/dist/hooks.d.ts +88 -21
- package/dist/hooks.mjs +24 -11
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +38 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -21
- package/dist/index.d.ts +94 -21
- package/dist/index.mjs +38 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/auth/context/AccountsContext.tsx +8 -1
- package/src/auth/context/AuthContext.tsx +31 -8
- package/src/auth/context/types.ts +8 -1
- package/src/auth/hooks/index.ts +29 -5
- package/src/auth/hooks/useAuthForm.ts +292 -226
- package/src/auth/hooks/useAuthFormState.ts +60 -0
- package/src/auth/hooks/useAuthValidation.ts +77 -0
- package/src/auth/hooks/useGithubAuth.ts +26 -5
- package/src/auth/hooks/useTwoFactor.ts +239 -0
- package/src/auth/hooks/useTwoFactorSetup.ts +213 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/types/form.ts +194 -0
- package/src/auth/types/index.ts +28 -0
- package/src/clients.ts +10 -0
- package/src/generated/cfg_accounts/_utils/schemas/OAuthTokenResponse.schema.ts +26 -3
- package/src/generated/cfg_accounts/_utils/schemas/OTPVerifyResponse.schema.ts +26 -3
- package/src/generated/cfg_accounts/accounts/client.ts +4 -1
- package/src/generated/cfg_accounts/accounts/models.ts +15 -6
- package/src/generated/cfg_accounts/accounts__oauth/models.ts +16 -7
- package/src/generated/cfg_accounts/client.ts +5 -2
- package/src/generated/cfg_accounts/http.ts +8 -2
- package/src/generated/cfg_accounts/schema.json +47 -19
- package/src/generated/cfg_centrifugo/client.ts +5 -2
- package/src/generated/cfg_centrifugo/http.ts +8 -2
- package/src/generated/cfg_totp/CLAUDE.md +12 -12
- package/src/generated/cfg_totp/_utils/fetchers/index.ts +3 -3
- package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_management.ts → totp__totp_management.ts} +3 -3
- package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_setup.ts → totp__totp_setup.ts} +3 -3
- package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_verification.ts → totp__totp_verification.ts} +3 -3
- package/src/generated/cfg_totp/_utils/hooks/index.ts +3 -3
- package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_management.ts → totp__totp_management.ts} +2 -2
- package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_setup.ts → totp__totp_setup.ts} +2 -2
- package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_verification.ts → totp__totp_verification.ts} +2 -2
- package/src/generated/cfg_totp/_utils/schemas/DeviceList.schema.ts +1 -1
- package/src/generated/cfg_totp/client.ts +14 -11
- package/src/generated/cfg_totp/http.ts +8 -2
- package/src/generated/cfg_totp/index.ts +16 -16
- package/src/generated/cfg_totp/schema.json +8 -7
- package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/client.ts +2 -2
- package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/models.ts +1 -1
- package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/client.ts +4 -4
- package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/client.ts +2 -2
- package/src/generated/cfg_webpush/client.ts +5 -2
- package/src/generated/cfg_webpush/http.ts +8 -2
- /package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/index.ts +0 -0
- /package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/index.ts +0 -0
- /package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/models.ts +0 -0
- /package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/index.ts +0 -0
- /package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/models.ts +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Form Types
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for auth form state and handlers.
|
|
5
|
+
* Used by both @djangocfg/api/auth hooks and @djangocfg/layouts/AuthLayout.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MutableRefObject } from 'react';
|
|
9
|
+
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
// Channel & Step Types
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export type AuthChannel = 'email' | 'phone';
|
|
15
|
+
export type AuthStep = 'identifier' | 'otp' | '2fa' | '2fa-setup' | 'success';
|
|
16
|
+
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
// Form State
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export interface AuthFormState {
|
|
22
|
+
/** Email or phone number */
|
|
23
|
+
identifier: string;
|
|
24
|
+
/** Current auth channel */
|
|
25
|
+
channel: AuthChannel;
|
|
26
|
+
/** OTP code input */
|
|
27
|
+
otp: string;
|
|
28
|
+
/** Loading state */
|
|
29
|
+
isLoading: boolean;
|
|
30
|
+
/** Terms acceptance state */
|
|
31
|
+
acceptedTerms: boolean;
|
|
32
|
+
/** Current form step */
|
|
33
|
+
step: AuthStep;
|
|
34
|
+
/** Error message */
|
|
35
|
+
error: string;
|
|
36
|
+
/** 2FA session ID from OTP/OAuth verification */
|
|
37
|
+
twoFactorSessionId: string | null;
|
|
38
|
+
/** Whether user should be prompted to enable 2FA */
|
|
39
|
+
shouldPrompt2FA: boolean;
|
|
40
|
+
/** 2FA code input */
|
|
41
|
+
twoFactorCode: string;
|
|
42
|
+
/** Using backup code instead of TOTP */
|
|
43
|
+
useBackupCode: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
// Form Handlers
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
export interface AuthFormStateHandlers {
|
|
51
|
+
setIdentifier: (identifier: string) => void;
|
|
52
|
+
setChannel: (channel: AuthChannel) => void;
|
|
53
|
+
setOtp: (otp: string) => void;
|
|
54
|
+
setAcceptedTerms: (accepted: boolean) => void;
|
|
55
|
+
setError: (error: string) => void;
|
|
56
|
+
clearError: () => void;
|
|
57
|
+
setStep: (step: AuthStep) => void;
|
|
58
|
+
setIsLoading: (loading: boolean) => void;
|
|
59
|
+
setTwoFactorSessionId: (sessionId: string | null) => void;
|
|
60
|
+
setShouldPrompt2FA: (prompt: boolean) => void;
|
|
61
|
+
setTwoFactorCode: (code: string) => void;
|
|
62
|
+
setUseBackupCode: (useBackup: boolean) => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface AuthFormSubmitHandlers {
|
|
66
|
+
handleIdentifierSubmit: (e: React.FormEvent) => Promise<void>;
|
|
67
|
+
handleOTPSubmit: (e: React.FormEvent) => Promise<void>;
|
|
68
|
+
handleResendOTP: () => Promise<void>;
|
|
69
|
+
handleBackToIdentifier: () => void;
|
|
70
|
+
forceOTPStep: () => void;
|
|
71
|
+
/** Handle 2FA TOTP/backup code verification */
|
|
72
|
+
handle2FASubmit: (e: React.FormEvent) => Promise<void>;
|
|
73
|
+
/** Switch to backup code input */
|
|
74
|
+
handleUseBackupCode: () => void;
|
|
75
|
+
/** Switch back to TOTP input */
|
|
76
|
+
handleUseTOTP: () => void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
80
|
+
// Validation
|
|
81
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
export interface AuthFormValidation {
|
|
84
|
+
/** Detect channel from identifier string */
|
|
85
|
+
detectChannelFromIdentifier: (identifier: string) => AuthChannel | null;
|
|
86
|
+
/** Validate identifier format */
|
|
87
|
+
validateIdentifier: (identifier: string, channel?: AuthChannel) => boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
// Auto-submit (for URL-based OTP)
|
|
92
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
export interface AuthFormAutoSubmit {
|
|
95
|
+
/** Ref to track if auto-submit from URL is in progress */
|
|
96
|
+
isAutoSubmittingFromUrl: MutableRefObject<boolean>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
100
|
+
// 2FA State (from useTwoFactor hook)
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export interface AuthForm2FAState {
|
|
104
|
+
/** Loading state for 2FA verification */
|
|
105
|
+
is2FALoading: boolean;
|
|
106
|
+
/** Warning message from 2FA (e.g., low backup codes) */
|
|
107
|
+
twoFactorWarning: string | null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
111
|
+
// Combined Form Interface
|
|
112
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
export interface AuthFormReturn extends
|
|
115
|
+
AuthFormState,
|
|
116
|
+
AuthFormStateHandlers,
|
|
117
|
+
AuthFormSubmitHandlers,
|
|
118
|
+
AuthFormValidation,
|
|
119
|
+
AuthFormAutoSubmit,
|
|
120
|
+
AuthForm2FAState {}
|
|
121
|
+
|
|
122
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
123
|
+
// Hook Options
|
|
124
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
export interface UseAuthFormOptions {
|
|
127
|
+
/** Callback when identifier step succeeds */
|
|
128
|
+
onIdentifierSuccess?: (identifier: string, channel: AuthChannel) => void;
|
|
129
|
+
/** Callback when OTP verification succeeds */
|
|
130
|
+
onOTPSuccess?: () => void;
|
|
131
|
+
/** Callback on any error */
|
|
132
|
+
onError?: (message: string) => void;
|
|
133
|
+
/** Source URL for tracking */
|
|
134
|
+
sourceUrl: string;
|
|
135
|
+
/** URL to redirect after successful OTP verification */
|
|
136
|
+
redirectUrl?: string;
|
|
137
|
+
/** If true, user must accept terms before submitting. Default: false */
|
|
138
|
+
requireTermsAcceptance?: boolean;
|
|
139
|
+
/** Path to auth page for auto-OTP detection. Default: '/auth' */
|
|
140
|
+
authPath?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
144
|
+
// Layout Context (UI-specific additions)
|
|
145
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
export interface AuthLayoutConfig {
|
|
148
|
+
/** Support page URL */
|
|
149
|
+
supportUrl?: string;
|
|
150
|
+
/** Terms of service URL */
|
|
151
|
+
termsUrl?: string;
|
|
152
|
+
/** Privacy policy URL */
|
|
153
|
+
privacyUrl?: string;
|
|
154
|
+
/** Source URL for tracking */
|
|
155
|
+
sourceUrl: string;
|
|
156
|
+
/** Enable phone authentication tab */
|
|
157
|
+
enablePhoneAuth?: boolean;
|
|
158
|
+
/** Enable GitHub OAuth button */
|
|
159
|
+
enableGithubAuth?: boolean;
|
|
160
|
+
/** Logo URL for success screen (SVG recommended) */
|
|
161
|
+
logoUrl?: string;
|
|
162
|
+
/** URL to redirect after successful auth (default: /dashboard) */
|
|
163
|
+
redirectUrl?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface AuthFormContextType extends AuthFormReturn, AuthLayoutConfig {}
|
|
167
|
+
|
|
168
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
169
|
+
// Layout Props
|
|
170
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
export interface AuthLayoutProps extends AuthLayoutConfig {
|
|
173
|
+
children?: React.ReactNode;
|
|
174
|
+
className?: string;
|
|
175
|
+
/** URL to redirect after successful auth (default: /dashboard) */
|
|
176
|
+
redirectUrl?: string;
|
|
177
|
+
/** Callback when identifier step succeeds */
|
|
178
|
+
onIdentifierSuccess?: (identifier: string, channel: AuthChannel) => void;
|
|
179
|
+
/** Callback when OTP verification succeeds */
|
|
180
|
+
onOTPSuccess?: () => void;
|
|
181
|
+
/** Callback when OAuth succeeds */
|
|
182
|
+
onOAuthSuccess?: (user: any, isNewUser: boolean, provider: string) => void;
|
|
183
|
+
/** Callback on any error */
|
|
184
|
+
onError?: (message: string) => void;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
188
|
+
// Help Component Props
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
export interface AuthHelpProps {
|
|
192
|
+
className?: string;
|
|
193
|
+
variant?: 'default' | 'compact';
|
|
194
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Types - Single source of truth
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Form types (for auth forms and layouts)
|
|
6
|
+
export type {
|
|
7
|
+
AuthChannel,
|
|
8
|
+
AuthStep,
|
|
9
|
+
AuthFormState,
|
|
10
|
+
AuthFormStateHandlers,
|
|
11
|
+
AuthFormSubmitHandlers,
|
|
12
|
+
AuthFormValidation,
|
|
13
|
+
AuthFormAutoSubmit,
|
|
14
|
+
AuthFormReturn,
|
|
15
|
+
UseAuthFormOptions,
|
|
16
|
+
AuthLayoutConfig,
|
|
17
|
+
AuthFormContextType,
|
|
18
|
+
AuthLayoutProps,
|
|
19
|
+
AuthHelpProps,
|
|
20
|
+
} from './form';
|
|
21
|
+
|
|
22
|
+
// Re-export context types
|
|
23
|
+
export type {
|
|
24
|
+
UserProfile,
|
|
25
|
+
AuthConfig,
|
|
26
|
+
AuthContextType,
|
|
27
|
+
AuthProviderProps,
|
|
28
|
+
} from '../context/types';
|
package/src/clients.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { API as AccountsAPIClass, LocalStorageAdapter } from './generated/cfg_accounts';
|
|
12
12
|
import { API as CentrifugoAPIClass } from './generated/cfg_centrifugo';
|
|
13
|
+
import { API as TotpAPIClass } from './generated/cfg_totp';
|
|
13
14
|
import { API as WebPushAPIClass } from './generated/cfg_webpush';
|
|
14
15
|
|
|
15
16
|
// ============================================================================
|
|
@@ -20,6 +21,7 @@ const apiUrl = isStaticBuild ? '' : process.env.NEXT_PUBLIC_API_URL || '';
|
|
|
20
21
|
const storage = new LocalStorageAdapter();
|
|
21
22
|
|
|
22
23
|
export const apiAccounts = new AccountsAPIClass(apiUrl, { storage });
|
|
24
|
+
export const apiTotp = new TotpAPIClass(apiUrl, { storage });
|
|
23
25
|
export const apiWebPush = new WebPushAPIClass(apiUrl, { storage });
|
|
24
26
|
export const apiCentrifugo = new CentrifugoAPIClass(apiUrl, { storage });
|
|
25
27
|
|
|
@@ -49,9 +51,17 @@ export * as CentrifugoHooks from './generated/cfg_centrifugo/_utils/hooks';
|
|
|
49
51
|
export * as CentrifugoFetchers from './generated/cfg_centrifugo/_utils/fetchers';
|
|
50
52
|
export * as CentrifugoTypes from './generated/cfg_centrifugo/_utils/schemas';
|
|
51
53
|
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// CFG TOTP (namespaced to avoid conflicts)
|
|
56
|
+
// ============================================================================
|
|
57
|
+
export * as TotpHooks from './generated/cfg_totp/_utils/hooks';
|
|
58
|
+
export * as TotpFetchers from './generated/cfg_totp/_utils/fetchers';
|
|
59
|
+
export * as TotpTypes from './generated/cfg_totp/_utils/schemas';
|
|
60
|
+
|
|
52
61
|
// ============================================================================
|
|
53
62
|
// API Classes (for creating custom instances)
|
|
54
63
|
// ============================================================================
|
|
55
64
|
export { API as AccountsAPI } from './generated/cfg_accounts';
|
|
65
|
+
export { API as TotpAPI } from './generated/cfg_totp';
|
|
56
66
|
export { API as WebPushAPI } from './generated/cfg_webpush';
|
|
57
67
|
export { API as CentrifugoAPI } from './generated/cfg_centrifugo';
|
|
@@ -3,18 +3,41 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This schema provides runtime validation and type inference.
|
|
5
5
|
* * Response with JWT tokens after OAuth authentication.
|
|
6
|
+
|
|
7
|
+
When 2FA is required:
|
|
8
|
+
- requires_2fa: True
|
|
9
|
+
- session_id: UUID of 2FA verification session
|
|
10
|
+
- access/refresh/user: null
|
|
11
|
+
|
|
12
|
+
When 2FA is not required:
|
|
13
|
+
- requires_2fa: False
|
|
14
|
+
- session_id: null
|
|
15
|
+
- access/refresh/user: populated
|
|
6
16
|
* */
|
|
7
17
|
import { z } from 'zod'
|
|
8
18
|
|
|
9
19
|
/**
|
|
10
20
|
* Response with JWT tokens after OAuth authentication.
|
|
21
|
+
|
|
22
|
+
When 2FA is required:
|
|
23
|
+
- requires_2fa: True
|
|
24
|
+
- session_id: UUID of 2FA verification session
|
|
25
|
+
- access/refresh/user: null
|
|
26
|
+
|
|
27
|
+
When 2FA is not required:
|
|
28
|
+
- requires_2fa: False
|
|
29
|
+
- session_id: null
|
|
30
|
+
- access/refresh/user: populated
|
|
11
31
|
*/
|
|
12
32
|
export const OAuthTokenResponseSchema = z.object({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
requires_2fa: z.boolean().optional(),
|
|
34
|
+
session_id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).nullable().optional(),
|
|
35
|
+
access: z.string().nullable().optional(),
|
|
36
|
+
refresh: z.string().nullable().optional(),
|
|
37
|
+
user: z.record(z.string(), z.any()).nullable().optional(),
|
|
16
38
|
is_new_user: z.boolean(),
|
|
17
39
|
is_new_connection: z.boolean(),
|
|
40
|
+
should_prompt_2fa: z.boolean().optional(),
|
|
18
41
|
})
|
|
19
42
|
|
|
20
43
|
/**
|
|
@@ -3,17 +3,40 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This schema provides runtime validation and type inference.
|
|
5
5
|
* * OTP verification response.
|
|
6
|
+
|
|
7
|
+
When 2FA is required:
|
|
8
|
+
- requires_2fa: True
|
|
9
|
+
- session_id: UUID of 2FA verification session
|
|
10
|
+
- refresh/access/user: null
|
|
11
|
+
|
|
12
|
+
When 2FA is not required:
|
|
13
|
+
- requires_2fa: False
|
|
14
|
+
- session_id: null
|
|
15
|
+
- refresh/access/user: populated
|
|
6
16
|
* */
|
|
7
17
|
import { z } from 'zod'
|
|
8
18
|
import { UserSchema } from './User.schema'
|
|
9
19
|
|
|
10
20
|
/**
|
|
11
21
|
* OTP verification response.
|
|
22
|
+
|
|
23
|
+
When 2FA is required:
|
|
24
|
+
- requires_2fa: True
|
|
25
|
+
- session_id: UUID of 2FA verification session
|
|
26
|
+
- refresh/access/user: null
|
|
27
|
+
|
|
28
|
+
When 2FA is not required:
|
|
29
|
+
- requires_2fa: False
|
|
30
|
+
- session_id: null
|
|
31
|
+
- refresh/access/user: populated
|
|
12
32
|
*/
|
|
13
33
|
export const OTPVerifyResponseSchema = z.object({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
requires_2fa: z.boolean().optional(),
|
|
35
|
+
session_id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).nullable().optional(),
|
|
36
|
+
refresh: z.string().nullable().optional(),
|
|
37
|
+
access: z.string().nullable().optional(),
|
|
38
|
+
user: UserSchema.nullable().optional(),
|
|
39
|
+
should_prompt_2fa: z.boolean().optional(),
|
|
17
40
|
})
|
|
18
41
|
|
|
19
42
|
/**
|
|
@@ -20,7 +20,10 @@ export class Accounts {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Verify OTP code and return JWT tokens.
|
|
23
|
+
* Verify OTP code and return JWT tokens or 2FA session. If user has 2FA
|
|
24
|
+
* enabled: - Returns requires_2fa=True with session_id - Client must
|
|
25
|
+
* complete 2FA verification at /cfg/totp/verify/ If user has no 2FA: -
|
|
26
|
+
* Returns JWT tokens and user data directly
|
|
24
27
|
*/
|
|
25
28
|
async otpVerifyCreate(data: Models.OTPVerifyRequest): Promise<Models.OTPVerifyResponse> {
|
|
26
29
|
const response = await this.client.request('POST', "/cfg/accounts/otp/verify/", { body: data });
|
|
@@ -57,16 +57,25 @@ export interface OTPVerifyRequest {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* OTP verification response.
|
|
60
|
+
* OTP verification response. When 2FA is required: - requires_2fa: True -
|
|
61
|
+
* session_id: UUID of 2FA verification session - refresh/access/user: null
|
|
62
|
+
* When 2FA is not required: - requires_2fa: False - session_id: null -
|
|
63
|
+
* refresh/access/user: populated
|
|
61
64
|
*
|
|
62
65
|
* Response model (includes read-only fields).
|
|
63
66
|
*/
|
|
64
67
|
export interface OTPVerifyResponse {
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
/** Whether 2FA verification is required */
|
|
69
|
+
requires_2fa?: boolean;
|
|
70
|
+
/** 2FA session ID (if requires_2fa is True) */
|
|
71
|
+
session_id?: string | null;
|
|
72
|
+
/** JWT refresh token (if requires_2fa is False) */
|
|
73
|
+
refresh?: string | null;
|
|
74
|
+
/** JWT access token (if requires_2fa is False) */
|
|
75
|
+
access?: string | null;
|
|
76
|
+
user?: User | null;
|
|
77
|
+
/** Whether user should be prompted to enable 2FA */
|
|
78
|
+
should_prompt_2fa?: boolean;
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
/**
|
|
@@ -64,21 +64,30 @@ export interface OAuthCallbackRequestRequest {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Response with JWT tokens after OAuth authentication.
|
|
67
|
+
* Response with JWT tokens after OAuth authentication. When 2FA is required: -
|
|
68
|
+
* requires_2fa: True - session_id: UUID of 2FA verification session -
|
|
69
|
+
* access/refresh/user: null When 2FA is not required: - requires_2fa: False -
|
|
70
|
+
* session_id: null - access/refresh/user: populated
|
|
68
71
|
*
|
|
69
72
|
* Response model (includes read-only fields).
|
|
70
73
|
*/
|
|
71
74
|
export interface OAuthTokenResponse {
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
|
|
75
|
+
/** True if 2FA verification is required */
|
|
76
|
+
requires_2fa?: boolean;
|
|
77
|
+
/** 2FA session ID (only when requires_2fa=True) */
|
|
78
|
+
session_id?: string | null;
|
|
79
|
+
/** JWT access token (null when requires_2fa=True) */
|
|
80
|
+
access?: string | null;
|
|
81
|
+
/** JWT refresh token (null when requires_2fa=True) */
|
|
82
|
+
refresh?: string | null;
|
|
83
|
+
/** Authenticated user info (null when requires_2fa=True) */
|
|
84
|
+
user?: Record<string, any> | null;
|
|
78
85
|
/** True if a new user was created during this OAuth flow */
|
|
79
86
|
is_new_user: boolean;
|
|
80
87
|
/** True if a new OAuth connection was created */
|
|
81
88
|
is_new_connection: boolean;
|
|
89
|
+
/** True if user should be prompted to enable 2FA */
|
|
90
|
+
should_prompt_2fa?: boolean;
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
/**
|
|
@@ -89,6 +89,7 @@ export class APIClient {
|
|
|
89
89
|
params?: Record<string, any>;
|
|
90
90
|
body?: any;
|
|
91
91
|
formData?: FormData;
|
|
92
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
92
93
|
headers?: Record<string, string>;
|
|
93
94
|
}
|
|
94
95
|
): Promise<T> {
|
|
@@ -125,6 +126,7 @@ export class APIClient {
|
|
|
125
126
|
params?: Record<string, any>;
|
|
126
127
|
body?: any;
|
|
127
128
|
formData?: FormData;
|
|
129
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
128
130
|
headers?: Record<string, string>;
|
|
129
131
|
}
|
|
130
132
|
): Promise<T> {
|
|
@@ -138,8 +140,8 @@ export class APIClient {
|
|
|
138
140
|
...(options?.headers || {})
|
|
139
141
|
};
|
|
140
142
|
|
|
141
|
-
// Don't set Content-Type for FormData (browser will set it with boundary)
|
|
142
|
-
if (!options?.formData && !headers['Content-Type']) {
|
|
143
|
+
// Don't set Content-Type for FormData/binaryBody (browser will set it with boundary)
|
|
144
|
+
if (!options?.formData && !options?.binaryBody && !headers['Content-Type']) {
|
|
143
145
|
headers['Content-Type'] = 'application/json';
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -166,6 +168,7 @@ export class APIClient {
|
|
|
166
168
|
params: options?.params,
|
|
167
169
|
body: options?.body,
|
|
168
170
|
formData: options?.formData,
|
|
171
|
+
binaryBody: options?.binaryBody,
|
|
169
172
|
});
|
|
170
173
|
|
|
171
174
|
const duration = Date.now() - startTime;
|
|
@@ -14,6 +14,8 @@ export interface HttpRequest {
|
|
|
14
14
|
params?: Record<string, any>;
|
|
15
15
|
/** FormData for file uploads (multipart/form-data) */
|
|
16
16
|
formData?: FormData;
|
|
17
|
+
/** Binary data for octet-stream uploads */
|
|
18
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface HttpResponse<T = any> {
|
|
@@ -37,7 +39,7 @@ export interface HttpClientAdapter {
|
|
|
37
39
|
*/
|
|
38
40
|
export class FetchAdapter implements HttpClientAdapter {
|
|
39
41
|
async request<T = any>(request: HttpRequest): Promise<HttpResponse<T>> {
|
|
40
|
-
const { method, url, headers, body, params, formData } = request;
|
|
42
|
+
const { method, url, headers, body, params, formData, binaryBody } = request;
|
|
41
43
|
|
|
42
44
|
// Build URL with query params
|
|
43
45
|
let finalUrl = url;
|
|
@@ -58,12 +60,16 @@ export class FetchAdapter implements HttpClientAdapter {
|
|
|
58
60
|
const finalHeaders: Record<string, string> = { ...headers };
|
|
59
61
|
|
|
60
62
|
// Determine body and content-type
|
|
61
|
-
let requestBody: string | FormData | undefined;
|
|
63
|
+
let requestBody: string | FormData | Blob | ArrayBuffer | undefined;
|
|
62
64
|
|
|
63
65
|
if (formData) {
|
|
64
66
|
// For multipart/form-data, let browser set Content-Type with boundary
|
|
65
67
|
requestBody = formData;
|
|
66
68
|
// Don't set Content-Type - browser will set it with boundary
|
|
69
|
+
} else if (binaryBody) {
|
|
70
|
+
// Binary upload (application/octet-stream)
|
|
71
|
+
finalHeaders['Content-Type'] = 'application/octet-stream';
|
|
72
|
+
requestBody = binaryBody;
|
|
67
73
|
} else if (body) {
|
|
68
74
|
// JSON request
|
|
69
75
|
finalHeaders['Content-Type'] = 'application/json';
|
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
"/cfg/accounts/otp/verify/": {
|
|
327
327
|
"post": {
|
|
328
328
|
"operationId": "cfg_accounts_otp_verify_create",
|
|
329
|
-
"description": "Verify OTP code and return JWT tokens
|
|
329
|
+
"description": "Verify OTP code and return JWT tokens or 2FA session.\n\nIf user has 2FA enabled:\n- Returns requires_2fa=True with session_id\n- Client must complete 2FA verification at /cfg/totp/verify/\n\nIf user has no 2FA:\n- Returns JWT tokens and user data directly",
|
|
330
330
|
"tags": [
|
|
331
331
|
"accounts"
|
|
332
332
|
],
|
|
@@ -1078,20 +1078,34 @@
|
|
|
1078
1078
|
},
|
|
1079
1079
|
"OAuthTokenResponse": {
|
|
1080
1080
|
"type": "object",
|
|
1081
|
-
"description": "Response with JWT tokens after OAuth authentication
|
|
1081
|
+
"description": "Response with JWT tokens after OAuth authentication.\n\nWhen 2FA is required:\n- requires_2fa: True\n- session_id: UUID of 2FA verification session\n- access/refresh/user: null\n\nWhen 2FA is not required:\n- requires_2fa: False\n- session_id: null\n- access/refresh/user: populated",
|
|
1082
1082
|
"properties": {
|
|
1083
|
+
"requires_2fa": {
|
|
1084
|
+
"type": "boolean",
|
|
1085
|
+
"default": false,
|
|
1086
|
+
"description": "True if 2FA verification is required"
|
|
1087
|
+
},
|
|
1088
|
+
"session_id": {
|
|
1089
|
+
"type": "string",
|
|
1090
|
+
"format": "uuid",
|
|
1091
|
+
"nullable": true,
|
|
1092
|
+
"description": "2FA session ID (only when requires_2fa=True)"
|
|
1093
|
+
},
|
|
1083
1094
|
"access": {
|
|
1084
1095
|
"type": "string",
|
|
1085
|
-
"
|
|
1096
|
+
"nullable": true,
|
|
1097
|
+
"description": "JWT access token (null when requires_2fa=True)"
|
|
1086
1098
|
},
|
|
1087
1099
|
"refresh": {
|
|
1088
1100
|
"type": "string",
|
|
1089
|
-
"
|
|
1101
|
+
"nullable": true,
|
|
1102
|
+
"description": "JWT refresh token (null when requires_2fa=True)"
|
|
1090
1103
|
},
|
|
1091
1104
|
"user": {
|
|
1092
1105
|
"type": "object",
|
|
1093
1106
|
"additionalProperties": {},
|
|
1094
|
-
"
|
|
1107
|
+
"nullable": true,
|
|
1108
|
+
"description": "Authenticated user info (null when requires_2fa=True)"
|
|
1095
1109
|
},
|
|
1096
1110
|
"is_new_user": {
|
|
1097
1111
|
"type": "boolean",
|
|
@@ -1100,14 +1114,15 @@
|
|
|
1100
1114
|
"is_new_connection": {
|
|
1101
1115
|
"type": "boolean",
|
|
1102
1116
|
"description": "True if a new OAuth connection was created"
|
|
1117
|
+
},
|
|
1118
|
+
"should_prompt_2fa": {
|
|
1119
|
+
"type": "boolean",
|
|
1120
|
+
"description": "True if user should be prompted to enable 2FA"
|
|
1103
1121
|
}
|
|
1104
1122
|
},
|
|
1105
1123
|
"required": [
|
|
1106
|
-
"access",
|
|
1107
1124
|
"is_new_connection",
|
|
1108
|
-
"is_new_user"
|
|
1109
|
-
"refresh",
|
|
1110
|
-
"user"
|
|
1125
|
+
"is_new_user"
|
|
1111
1126
|
]
|
|
1112
1127
|
},
|
|
1113
1128
|
"OTPErrorResponse": {
|
|
@@ -1200,15 +1215,28 @@
|
|
|
1200
1215
|
},
|
|
1201
1216
|
"OTPVerifyResponse": {
|
|
1202
1217
|
"type": "object",
|
|
1203
|
-
"description": "OTP verification response
|
|
1218
|
+
"description": "OTP verification response.\n\nWhen 2FA is required:\n- requires_2fa: True\n- session_id: UUID of 2FA verification session\n- refresh/access/user: null\n\nWhen 2FA is not required:\n- requires_2fa: False\n- session_id: null\n- refresh/access/user: populated",
|
|
1204
1219
|
"properties": {
|
|
1220
|
+
"requires_2fa": {
|
|
1221
|
+
"type": "boolean",
|
|
1222
|
+
"default": false,
|
|
1223
|
+
"description": "Whether 2FA verification is required"
|
|
1224
|
+
},
|
|
1225
|
+
"session_id": {
|
|
1226
|
+
"type": "string",
|
|
1227
|
+
"format": "uuid",
|
|
1228
|
+
"nullable": true,
|
|
1229
|
+
"description": "2FA session ID (if requires_2fa is True)"
|
|
1230
|
+
},
|
|
1205
1231
|
"refresh": {
|
|
1206
1232
|
"type": "string",
|
|
1207
|
-
"
|
|
1233
|
+
"nullable": true,
|
|
1234
|
+
"description": "JWT refresh token (if requires_2fa is False)"
|
|
1208
1235
|
},
|
|
1209
1236
|
"access": {
|
|
1210
1237
|
"type": "string",
|
|
1211
|
-
"
|
|
1238
|
+
"nullable": true,
|
|
1239
|
+
"description": "JWT access token (if requires_2fa is False)"
|
|
1212
1240
|
},
|
|
1213
1241
|
"user": {
|
|
1214
1242
|
"allOf": [
|
|
@@ -1216,14 +1244,14 @@
|
|
|
1216
1244
|
"$ref": "#/components/schemas/User"
|
|
1217
1245
|
}
|
|
1218
1246
|
],
|
|
1219
|
-
"
|
|
1247
|
+
"nullable": true,
|
|
1248
|
+
"description": "User information (if requires_2fa is False)"
|
|
1249
|
+
},
|
|
1250
|
+
"should_prompt_2fa": {
|
|
1251
|
+
"type": "boolean",
|
|
1252
|
+
"description": "Whether user should be prompted to enable 2FA"
|
|
1220
1253
|
}
|
|
1221
|
-
}
|
|
1222
|
-
"required": [
|
|
1223
|
-
"access",
|
|
1224
|
-
"refresh",
|
|
1225
|
-
"user"
|
|
1226
|
-
]
|
|
1254
|
+
}
|
|
1227
1255
|
},
|
|
1228
1256
|
"PatchedUserProfileUpdateRequest": {
|
|
1229
1257
|
"type": "object",
|
|
@@ -89,6 +89,7 @@ export class APIClient {
|
|
|
89
89
|
params?: Record<string, any>;
|
|
90
90
|
body?: any;
|
|
91
91
|
formData?: FormData;
|
|
92
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
92
93
|
headers?: Record<string, string>;
|
|
93
94
|
}
|
|
94
95
|
): Promise<T> {
|
|
@@ -125,6 +126,7 @@ export class APIClient {
|
|
|
125
126
|
params?: Record<string, any>;
|
|
126
127
|
body?: any;
|
|
127
128
|
formData?: FormData;
|
|
129
|
+
binaryBody?: Blob | ArrayBuffer;
|
|
128
130
|
headers?: Record<string, string>;
|
|
129
131
|
}
|
|
130
132
|
): Promise<T> {
|
|
@@ -138,8 +140,8 @@ export class APIClient {
|
|
|
138
140
|
...(options?.headers || {})
|
|
139
141
|
};
|
|
140
142
|
|
|
141
|
-
// Don't set Content-Type for FormData (browser will set it with boundary)
|
|
142
|
-
if (!options?.formData && !headers['Content-Type']) {
|
|
143
|
+
// Don't set Content-Type for FormData/binaryBody (browser will set it with boundary)
|
|
144
|
+
if (!options?.formData && !options?.binaryBody && !headers['Content-Type']) {
|
|
143
145
|
headers['Content-Type'] = 'application/json';
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -166,6 +168,7 @@ export class APIClient {
|
|
|
166
168
|
params: options?.params,
|
|
167
169
|
body: options?.body,
|
|
168
170
|
formData: options?.formData,
|
|
171
|
+
binaryBody: options?.binaryBody,
|
|
169
172
|
});
|
|
170
173
|
|
|
171
174
|
const duration = Date.now() - startTime;
|