@djangocfg/api 2.1.481 → 2.1.483

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.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/dist/auth.cjs +64 -25
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +14 -1
  5. package/dist/auth.d.ts +14 -1
  6. package/dist/auth.mjs +64 -25
  7. package/dist/auth.mjs.map +1 -1
  8. package/dist/clients.cjs +29 -8
  9. package/dist/clients.cjs.map +1 -1
  10. package/dist/clients.d.cts +22 -2
  11. package/dist/clients.d.ts +22 -2
  12. package/dist/clients.mjs +29 -8
  13. package/dist/clients.mjs.map +1 -1
  14. package/dist/hooks.cjs +29 -8
  15. package/dist/hooks.cjs.map +1 -1
  16. package/dist/hooks.mjs +29 -8
  17. package/dist/hooks.mjs.map +1 -1
  18. package/dist/index.cjs +31 -9
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +32 -4
  21. package/dist/index.d.ts +32 -4
  22. package/dist/index.mjs +31 -9
  23. package/dist/index.mjs.map +1 -1
  24. package/package.json +2 -2
  25. package/src/_api/generated/_cfg_accounts/api.ts +3 -3
  26. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpConsentPolicyRetrieve.ts +2 -2
  27. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpRequestCreate.ts +2 -2
  28. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpVerifyCreate.ts +2 -2
  29. package/src/_api/generated/_cfg_accounts/index.ts +1 -1
  30. package/src/_api/generated/_cfg_accounts/openapi.json +21 -43
  31. package/src/_api/generated/_cfg_accounts/schemas/OAuthCallbackRequestRequest.ts +1 -0
  32. package/src/_api/generated/_cfg_accounts/schemas/OAuthTokenResponse.ts +1 -0
  33. package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyRequest.ts +1 -0
  34. package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyResponse.ts +1 -0
  35. package/src/_api/generated/_cfg_totp/openapi.json +5 -0
  36. package/src/_api/generated/_cfg_totp/schemas/VerifyResponse.ts +1 -0
  37. package/src/_api/generated/helpers/auth.ts +27 -6
  38. package/src/_api/generated/index.ts +1 -1
  39. package/src/_api/generated/openapi.json +26 -43
  40. package/src/_api/generated/sdk.gen.ts +1 -1
  41. package/src/_api/generated/types.gen.ts +28 -0
  42. package/src/auth/context/AccountsContext.tsx +1 -0
  43. package/src/auth/context/AuthContext.tsx +3 -2
  44. package/src/auth/context/types.ts +1 -1
  45. package/src/auth/hooks/useAuthForm.ts +3 -2
  46. package/src/auth/hooks/useAuthFormState.ts +3 -0
  47. package/src/auth/hooks/useAuthRedirect.ts +2 -2
  48. package/src/auth/hooks/useGithubAuth.ts +34 -21
  49. package/src/auth/hooks/useTwoFactorVerify.ts +5 -3
  50. package/src/auth/types/form.ts +3 -1
  51. package/src/index.ts +5 -1
@@ -49,7 +49,7 @@ import type { OtpRequestRequest } from '../../_api/generated/_cfg_accounts/types
49
49
  // everything downstream reads the RESOLVED values from `useAuth().routes`.
50
50
  const defaultRoutes = {
51
51
  auth: '/auth',
52
- defaultCallback: '/',
52
+ defaultCallback: '/private',
53
53
  defaultAuthCallback: '/auth',
54
54
  };
55
55
 
@@ -288,6 +288,7 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
288
288
  sourceUrl?: string,
289
289
  redirectUrl?: string,
290
290
  skipRedirect?: boolean,
291
+ rememberMe = false,
291
292
  ): Promise<{
292
293
  success: boolean;
293
294
  message: string;
@@ -305,6 +306,7 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
305
306
  identifier,
306
307
  otp: otpCode,
307
308
  source_url: sourceUrl,
309
+ remember_me: rememberMe,
308
310
  });
309
311
 
310
312
  if (result.requires_2fa && result.session_id) {
@@ -322,7 +324,6 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
322
324
  authLogger.error('Verify OTP returned invalid response:', result);
323
325
  return { success: false, message: 'Invalid OTP verification response' };
324
326
  }
325
-
326
327
  if (identifier.includes('@')) {
327
328
  setStoredEmail(identifier);
328
329
  }
@@ -50,7 +50,7 @@ export interface AuthContextType {
50
50
 
51
51
  // OTP Methods - email only
52
52
  requestOTP: (identifier: string, sourceUrl?: string, consent?: OTPRequestConsent) => Promise<OTPRequestResult>;
53
- verifyOTP: (identifier: string, otpCode: string, sourceUrl?: string, redirectUrl?: string, skipRedirect?: boolean) => Promise<{
53
+ verifyOTP: (identifier: string, otpCode: string, sourceUrl?: string, redirectUrl?: string, skipRedirect?: boolean, rememberMe?: boolean) => Promise<{
54
54
  success: boolean;
55
55
  message: string;
56
56
  user?: UserProfile;
@@ -54,6 +54,7 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
54
54
  isLoading,
55
55
  acceptedTerms,
56
56
  marketingConsent,
57
+ rememberMe,
57
58
  twoFactorSessionId,
58
59
  twoFactorCode,
59
60
  useBackupCode,
@@ -193,7 +194,7 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
193
194
 
194
195
  try {
195
196
  // Skip redirect - we'll show success screen first
196
- const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true);
197
+ const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true, rememberMe);
197
198
 
198
199
  // Check if 2FA is required (user has TOTP device)
199
200
  if (result.requires_2fa && result.session_id) {
@@ -228,7 +229,7 @@ export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
228
229
  } finally {
229
230
  setIsLoading(false);
230
231
  }
231
- }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
232
+ }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, rememberMe, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
232
233
 
233
234
  const handleOTPSubmit = useCallback(async (e: React.FormEvent) => {
234
235
  e.preventDefault();
@@ -26,6 +26,7 @@ export const useAuthFormState = (
26
26
  // null until the marketing-consent feature (if enabled) resolves its
27
27
  // jurisdiction-aware default — the layout owns that decision.
28
28
  const [marketingConsent, setMarketingConsent] = useState<boolean | null>(null);
29
+ const [rememberMe, setRememberMe] = useState(false);
29
30
  const [step, setStep] = useState<AuthStep>('identifier');
30
31
  const [error, setError] = useState('');
31
32
  const [webmail, setWebmail] = useState<WebmailLink | null>(null);
@@ -68,6 +69,7 @@ export const useAuthFormState = (
68
69
  isLoading,
69
70
  acceptedTerms,
70
71
  marketingConsent,
72
+ rememberMe,
71
73
  step,
72
74
  error,
73
75
  twoFactorSessionId,
@@ -83,6 +85,7 @@ export const useAuthFormState = (
83
85
  setOtp,
84
86
  setAcceptedTerms,
85
87
  setMarketingConsent,
88
+ setRememberMe,
86
89
  setError,
87
90
  clearError,
88
91
  setStep,
@@ -52,7 +52,7 @@ export const consumeSavedRedirect = (): string | null => {
52
52
  };
53
53
 
54
54
  export const useAuthRedirectManager = (options: AuthRedirectOptions = {}) => {
55
- const { fallbackUrl = '/dashboard', clearOnUse = true } = options;
55
+ const { fallbackUrl = '/private', clearOnUse = true } = options;
56
56
  const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage<string>(AUTH_REDIRECT_KEY, '');
57
57
 
58
58
  const setRedirect = (url: string) => {
@@ -101,4 +101,4 @@ export const useAuthRedirectManager = (options: AuthRedirectOptions = {}) => {
101
101
  getFinalRedirectUrl,
102
102
  useAndClearRedirect
103
103
  };
104
- };
104
+ };
@@ -10,6 +10,8 @@ import { useCfgRouter } from './useCfgRouter';
10
10
 
11
11
  export interface UseGithubAuthOptions {
12
12
  sourceUrl?: string;
13
+ /** Keep the completed browser session for 30 days. */
14
+ rememberMe?: boolean;
13
15
  onSuccess?: (user: any, isNewUser: boolean) => void;
14
16
  onError?: (error: string) => void;
15
17
  /** Callback when 2FA is required */
@@ -46,7 +48,7 @@ export interface UseGithubAuthReturn {
46
48
  * ```
47
49
  */
48
50
  export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuthReturn => {
49
- const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
51
+ const { sourceUrl, rememberMe = false, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
50
52
  const router = useCfgRouter();
51
53
 
52
54
  const [isLoading, setIsLoading] = useState(false);
@@ -58,6 +60,9 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
58
60
  const startGithubAuth = useCallback(async () => {
59
61
  setIsLoading(true);
60
62
  setError(null);
63
+ // Match the OTP flow: a new login must not leave an older persistent
64
+ // session available after this browser is intentionally made session-only.
65
+ auth.clearSession();
61
66
 
62
67
  try {
63
68
  authLogger.info('Starting GitHub OAuth flow...');
@@ -87,6 +92,7 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
87
92
  if (typeof window !== 'undefined') {
88
93
  sessionStorage.setItem('oauth_state', response.state);
89
94
  sessionStorage.setItem('oauth_provider', 'github');
95
+ sessionStorage.setItem('cfg.oauth.remember_me', String(rememberMe));
90
96
  }
91
97
 
92
98
  window.location.href = response.authorization_url;
@@ -105,7 +111,7 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
105
111
  } finally {
106
112
  setIsLoading(false);
107
113
  }
108
- }, [sourceUrl, onError]);
114
+ }, [sourceUrl, rememberMe, onError]);
109
115
 
110
116
  /**
111
117
  * Handle GitHub OAuth callback - exchanges code for JWT tokens.
@@ -121,34 +127,38 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
121
127
  authLogger.info('Processing GitHub OAuth callback...');
122
128
 
123
129
  // Verify state matches what we stored
130
+ let rememberMeForSession = false;
124
131
  if (typeof window !== 'undefined') {
125
132
  const storedState = sessionStorage.getItem('oauth_state');
126
133
  if (storedState && storedState !== state) {
127
134
  throw new Error('Invalid OAuth state - possible CSRF attack');
128
135
  }
136
+ rememberMeForSession = sessionStorage.getItem('cfg.oauth.remember_me') === 'true';
129
137
  // Clear stored state
130
138
  sessionStorage.removeItem('oauth_state');
131
139
  sessionStorage.removeItem('oauth_provider');
140
+ sessionStorage.removeItem('cfg.oauth.remember_me');
141
+
132
142
  }
133
143
 
134
144
  const result = await CfgAccountsOauth.cfgAccountsOauthGithubCallbackCreate({
135
- body: { code, state },
145
+ body: { code, state, remember_me: rememberMeForSession },
136
146
  throwOnError: true,
137
147
  });
138
148
  const response = result.data;
139
149
 
140
150
  // Check if 2FA is required
141
151
  if (response.requires_2fa && response.session_id) {
142
- authLogger.info('GitHub OAuth requires 2FA, session:', response.session_id);
152
+ authLogger.info('GitHub OAuth requires 2FA, session:', response.session_id);
143
153
 
144
- // Track 2FA requirement
145
- Analytics.event(AnalyticsEvent.AUTH_OAUTH_START, {
146
- category: AnalyticsCategory.AUTH,
147
- label: 'github-2fa-required',
148
- });
154
+ // Track 2FA requirement
155
+ Analytics.event(AnalyticsEvent.AUTH_OAUTH_START, {
156
+ category: AnalyticsCategory.AUTH,
157
+ label: 'github-2fa-required',
158
+ });
149
159
 
150
- // Call 2FA callback
151
- onRequires2FA?.(response.session_id, response.should_prompt_2fa || false);
160
+ // Call 2FA callback
161
+ onRequires2FA?.(response.session_id, response.should_prompt_2fa || false);
152
162
  return;
153
163
  }
154
164
 
@@ -158,30 +168,33 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
158
168
 
159
169
  authLogger.info('GitHub OAuth successful, user:', response.user);
160
170
 
161
- // The ONE write path — atomic pair + session notification.
171
+ auth.setStorageMode(response.persistent_session ? 'localStorage' : 'sessionStorage');
172
+ // The ONE write path — atomic pair + session notification.
162
173
  auth.setSession({ access: response.access, refresh: response.refresh });
163
174
 
164
- // Track successful OAuth
175
+ // Track successful OAuth
165
176
  Analytics.event(AnalyticsEvent.AUTH_LOGIN_SUCCESS, {
166
- category: AnalyticsCategory.AUTH,
167
- label: 'github',
177
+ category: AnalyticsCategory.AUTH,
178
+ label: 'github',
168
179
  });
169
180
 
170
- // Set user ID for future tracking
181
+ // Set user ID for future tracking
171
182
  if (response.user?.id) {
172
183
  Analytics.setUser(String(response.user.id));
173
184
  }
174
185
 
175
- // Call success callback
186
+ // Call success callback
176
187
  onSuccess?.(response.user, response.is_new_user || false);
177
188
 
178
- // Redirect (unless skipRedirect is true)
189
+ // Redirect (unless skipRedirect is true)
179
190
  if (!skipRedirect) {
180
- // Use hardPush for full page reload - ensures all React contexts reinitialize
181
- const finalRedirectUrl = redirectUrl || '/dashboard';
191
+ // Use hardPush for full page reload - ensures all React contexts reinitialize
192
+ const finalRedirectUrl = redirectUrl || '/private';
182
193
  router.hardPush(finalRedirectUrl);
183
194
  }
184
195
 
196
+ return;
197
+
185
198
  } catch (err) {
186
199
  const errorMessage = err instanceof Error ? err.message : 'GitHub authentication failed';
187
200
  authLogger.error('GitHub OAuth callback error:', err);
@@ -196,7 +209,7 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
196
209
  } finally {
197
210
  setIsLoading(false);
198
211
  }
199
- }, [onSuccess, onError, redirectUrl, router]);
212
+ }, [onSuccess, onError, onRequires2FA, redirectUrl, router, skipRedirect]);
200
213
 
201
214
  return {
202
215
  isLoading,
@@ -67,9 +67,11 @@ export const useTwoFactorVerify = (
67
67
  access_token: string;
68
68
  refresh_token: string;
69
69
  user: any;
70
- warning?: string;
71
- remaining_backup_codes?: number;
70
+ warning?: string;
71
+ remaining_backup_codes?: number;
72
+ persistent_session: boolean;
72
73
  }) => {
74
+ auth.setStorageMode(response.persistent_session ? 'localStorage' : 'sessionStorage');
73
75
  // Save tokens — the ONE write path (atomic pair + session notification).
74
76
  auth.setSession({ access: response.access_token, refresh: response.refresh_token });
75
77
 
@@ -92,7 +94,7 @@ export const useTwoFactorVerify = (
92
94
 
93
95
  // Redirect (unless skipRedirect). Priority: saved back-url > option > default.
94
96
  if (!skipRedirect) {
95
- const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || '/';
97
+ const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || '/private';
96
98
  authLogger.info('2FA successful, redirecting to:', finalRedirectUrl);
97
99
  router.hardPush(finalRedirectUrl);
98
100
  }
@@ -60,6 +60,8 @@ export interface AuthFormState {
60
60
  * jurisdiction-aware default on mount.
61
61
  */
62
62
  marketingConsent: boolean | null;
63
+ /** Keep this browser signed in for 30 days after successful verification. */
64
+ rememberMe: boolean;
63
65
  /** Current form step */
64
66
  step: AuthStep;
65
67
  /** Error message */
@@ -91,6 +93,7 @@ export interface AuthFormStateHandlers {
91
93
  setOtp: (otp: string) => void;
92
94
  setAcceptedTerms: (accepted: boolean) => void;
93
95
  setMarketingConsent: (consent: boolean | null) => void;
96
+ setRememberMe: (rememberMe: boolean) => void;
94
97
  setError: (error: string) => void;
95
98
  clearError: () => void;
96
99
  setStep: (step: AuthStep) => void;
@@ -191,4 +194,3 @@ export interface UseAuthFormOptions {
191
194
  /** Path to auth page for auto-OTP detection. Default: '/auth' */
192
195
  authPath?: string;
193
196
  }
194
-
package/src/index.ts CHANGED
@@ -14,7 +14,11 @@ export * from './_api/generated';
14
14
  export {
15
15
  CfgAccountsApiKey,
16
16
  CfgAccountsOauth,
17
- CfgAccounts,
17
+ // Compatibility alias: the OTP operations were split out of the legacy
18
+ // `CfgAccounts` generated class, but this package's public API keeps that
19
+ // import stable for existing applications.
20
+ CfgAccountsOtp as CfgAccounts,
21
+ CfgAccountsOtp,
18
22
  CfgAccountsProfile,
19
23
  CfgAccountsAuth,
20
24
  CfgCentrifugo,