@djangocfg/api 2.1.547 → 2.1.549

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.d.cts CHANGED
@@ -1232,7 +1232,9 @@ declare const formatAuthError: (error: unknown) => string;
1232
1232
  *
1233
1233
  * Invariant that fixes the bug: once auth is resolved (`authLoading === false`)
1234
1234
  * and the user is unauthenticated, `isLoading` must become false so the guard
1235
- * redirects to the form instead of spinning forever.
1235
+ * redirects to the form instead of spinning forever. The same invariant covers
1236
+ * the forbidden case: a signed-in user without the required privilege must stop
1237
+ * loading too, or the preloader spins on a state no redirect will ever clear.
1236
1238
  */
1237
1239
  interface GuardInput {
1238
1240
  /** First client render matches SSR (preloader) until mounted, to avoid hydration mismatch. */
@@ -1245,7 +1247,20 @@ interface GuardInput {
1245
1247
  isRedirecting: boolean;
1246
1248
  /** User currently has a (valid-looking) session. */
1247
1249
  isAuthenticated: boolean;
1250
+ /** Route requires staff/admin privileges, not just a session. */
1251
+ requireStaff?: boolean;
1252
+ /** `is_staff || is_superuser`, from `useAuth().isAdminUser`. */
1253
+ isAdminUser?: boolean;
1248
1254
  }
1255
+ /**
1256
+ * True when the session is valid but lacks the required privilege.
1257
+ *
1258
+ * Distinct from unauthenticated: the fix is a different account, not signing
1259
+ * in, so this must never redirect to the auth form. Redirecting would also
1260
+ * loop — the guard saves the current URL first, so a successful re-login on
1261
+ * the same account returns straight to the wall.
1262
+ */
1263
+ declare function isForbidden(s: GuardInput): boolean;
1249
1264
  /**
1250
1265
  * True while the guard should render a preloader rather than the protected
1251
1266
  * content. Mirrors useAuthGuard's formula exactly.
@@ -1355,4 +1370,4 @@ declare const Analytics: {
1355
1370
  setUser(userId: string | null): void;
1356
1371
  };
1357
1372
 
1358
- export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AnalyticsSink, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestConsent, type OTPRequestResult, type PasswordLoginOptions, type PasswordLoginResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setAnalyticsSink, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
1373
+ export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AnalyticsSink, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestConsent, type OTPRequestResult, type PasswordLoginOptions, type PasswordLoginResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, isForbidden, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setAnalyticsSink, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
package/dist/auth.d.ts CHANGED
@@ -1232,7 +1232,9 @@ declare const formatAuthError: (error: unknown) => string;
1232
1232
  *
1233
1233
  * Invariant that fixes the bug: once auth is resolved (`authLoading === false`)
1234
1234
  * and the user is unauthenticated, `isLoading` must become false so the guard
1235
- * redirects to the form instead of spinning forever.
1235
+ * redirects to the form instead of spinning forever. The same invariant covers
1236
+ * the forbidden case: a signed-in user without the required privilege must stop
1237
+ * loading too, or the preloader spins on a state no redirect will ever clear.
1236
1238
  */
1237
1239
  interface GuardInput {
1238
1240
  /** First client render matches SSR (preloader) until mounted, to avoid hydration mismatch. */
@@ -1245,7 +1247,20 @@ interface GuardInput {
1245
1247
  isRedirecting: boolean;
1246
1248
  /** User currently has a (valid-looking) session. */
1247
1249
  isAuthenticated: boolean;
1250
+ /** Route requires staff/admin privileges, not just a session. */
1251
+ requireStaff?: boolean;
1252
+ /** `is_staff || is_superuser`, from `useAuth().isAdminUser`. */
1253
+ isAdminUser?: boolean;
1248
1254
  }
1255
+ /**
1256
+ * True when the session is valid but lacks the required privilege.
1257
+ *
1258
+ * Distinct from unauthenticated: the fix is a different account, not signing
1259
+ * in, so this must never redirect to the auth form. Redirecting would also
1260
+ * loop — the guard saves the current URL first, so a successful re-login on
1261
+ * the same account returns straight to the wall.
1262
+ */
1263
+ declare function isForbidden(s: GuardInput): boolean;
1249
1264
  /**
1250
1265
  * True while the guard should render a preloader rather than the protected
1251
1266
  * content. Mirrors useAuthGuard's formula exactly.
@@ -1355,4 +1370,4 @@ declare const Analytics: {
1355
1370
  setUser(userId: string | null): void;
1356
1371
  };
1357
1372
 
1358
- export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AnalyticsSink, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestConsent, type OTPRequestResult, type PasswordLoginOptions, type PasswordLoginResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setAnalyticsSink, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
1373
+ export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AnalyticsSink, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestConsent, type OTPRequestResult, type PasswordLoginOptions, type PasswordLoginResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, isForbidden, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setAnalyticsSink, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
package/dist/auth.mjs CHANGED
@@ -4540,6 +4540,7 @@ function AccountsProvider({ children }) {
4540
4540
  const [profileError, setProfileError] = useState10(null);
4541
4541
  const profileRef = useRef5(profile);
4542
4542
  const isLoadingRef = useRef5(false);
4543
+ const fetchSeqRef = useRef5(0);
4543
4544
  useEffect5(() => {
4544
4545
  profileRef.current = profile;
4545
4546
  }, [profile]);
@@ -4558,10 +4559,11 @@ function AccountsProvider({ children }) {
4558
4559
  authLogger.debug(`No API origin configured, skipping profile fetch (caller: ${callerId})`);
4559
4560
  return void 0;
4560
4561
  }
4561
- if (isLoadingRef.current) {
4562
+ if (isLoadingRef.current && !force) {
4562
4563
  authLogger.debug(`Profile loading in progress, skipping (caller: ${callerId})`);
4563
4564
  return profileRef.current;
4564
4565
  }
4566
+ const seq = ++fetchSeqRef.current;
4565
4567
  setIsLoadingProfile(true);
4566
4568
  isLoadingRef.current = true;
4567
4569
  setProfileError(null);
@@ -4569,17 +4571,20 @@ function AccountsProvider({ children }) {
4569
4571
  authLogger.debug(`Fetching profile from API (caller: ${callerId}, force: ${force})`);
4570
4572
  const res = await CfgAccountsProfile.cfgAccountsProfileRetrieve({ throwOnError: true });
4571
4573
  const result = res.data;
4574
+ if (seq !== fetchSeqRef.current) return result;
4572
4575
  setProfile(result);
4573
4576
  profileRef.current = result;
4574
4577
  setCachedProfile(result);
4575
4578
  return result;
4576
4579
  } catch (error) {
4577
4580
  const err = error instanceof Error ? error : new Error("Failed to fetch profile");
4578
- setProfileError(err);
4581
+ if (seq === fetchSeqRef.current) setProfileError(err);
4579
4582
  throw err;
4580
4583
  } finally {
4581
- setIsLoadingProfile(false);
4582
- isLoadingRef.current = false;
4584
+ if (seq === fetchSeqRef.current) {
4585
+ setIsLoadingProfile(false);
4586
+ isLoadingRef.current = false;
4587
+ }
4583
4588
  }
4584
4589
  }, []);
4585
4590
  const updateProfile = useCallback10(async (data) => {
@@ -5147,9 +5152,16 @@ var formatAuthError = /* @__PURE__ */ __name((error) => {
5147
5152
  }, "formatAuthError");
5148
5153
 
5149
5154
  // src/auth/utils/guard.ts
5155
+ function isForbidden(s) {
5156
+ return Boolean(
5157
+ s.mounted && s.requireAuth && s.requireStaff && !s.authLoading && s.isAuthenticated && !s.isAdminUser
5158
+ );
5159
+ }
5160
+ __name(isForbidden, "isForbidden");
5150
5161
  function resolveGuardIsLoading(s) {
5151
5162
  if (!s.mounted) return true;
5152
5163
  if (!s.requireAuth) return false;
5164
+ if (isForbidden(s)) return false;
5153
5165
  return s.authLoading || s.isRedirecting || !s.isAuthenticated;
5154
5166
  }
5155
5167
  __name(resolveGuardIsLoading, "resolveGuardIsLoading");
@@ -5180,6 +5192,7 @@ export {
5180
5192
  getCachedProfile,
5181
5193
  hasValidCache,
5182
5194
  isAllowedAuthPath,
5195
+ isForbidden,
5183
5196
  logger,
5184
5197
  normalizePath,
5185
5198
  peekSavedRedirect,