@djangocfg/api 2.1.547 → 2.1.548

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 CHANGED
@@ -77,6 +77,31 @@ import {
77
77
  } from '@djangocfg/api/auth';
78
78
  ```
79
79
 
80
+ ### Guard resolvers
81
+
82
+ The "spinner vs redirect vs render" decision is pure and separately testable.
83
+ `useAuthGuard` (and `@djangocfg/layouts`' `PrivateLayout`) just wire React state
84
+ into these:
85
+
86
+ ```typescript
87
+ import {
88
+ resolveGuardIsLoading, // show the preloader?
89
+ shouldRedirectToAuth, // no session → send to the auth form
90
+ isForbidden, // signed in, but lacks the required privilege
91
+ resolveGuardIsAuthenticated,
92
+ type GuardInput,
93
+ } from '@djangocfg/api/auth';
94
+ ```
95
+
96
+ `GuardInput` takes two optional fields for the role gate: `requireStaff` (the
97
+ route needs staff/admin) and `isAdminUser` (from `useAuth()` — the
98
+ `is_staff || is_superuser` fold, never one flag).
99
+
100
+ `isForbidden` is deliberately **not** a redirect condition: the session is
101
+ valid, so the auth form cannot fix it, and redirecting would loop on the saved
102
+ URL. Render a distinct "not authorised" state instead. `resolveGuardIsLoading`
103
+ returns `false` once forbidden, so the preloader never spins on it.
104
+
80
105
  ## Token storage & DPoP
81
106
 
82
107
  The token lives in the shared `auth` store (localStorage by default, switchable
package/dist/auth.cjs CHANGED
@@ -50,6 +50,7 @@ __export(auth_exports, {
50
50
  getCachedProfile: () => getCachedProfile,
51
51
  hasValidCache: () => hasValidCache,
52
52
  isAllowedAuthPath: () => isAllowedAuthPath,
53
+ isForbidden: () => isForbidden,
53
54
  logger: () => logger,
54
55
  normalizePath: () => normalizePath,
55
56
  peekSavedRedirect: () => peekSavedRedirect,
@@ -5210,9 +5211,16 @@ var formatAuthError = /* @__PURE__ */ __name((error) => {
5210
5211
  }, "formatAuthError");
5211
5212
 
5212
5213
  // src/auth/utils/guard.ts
5214
+ function isForbidden(s) {
5215
+ return Boolean(
5216
+ s.mounted && s.requireAuth && s.requireStaff && !s.authLoading && s.isAuthenticated && !s.isAdminUser
5217
+ );
5218
+ }
5219
+ __name(isForbidden, "isForbidden");
5213
5220
  function resolveGuardIsLoading(s) {
5214
5221
  if (!s.mounted) return true;
5215
5222
  if (!s.requireAuth) return false;
5223
+ if (isForbidden(s)) return false;
5216
5224
  return s.authLoading || s.isRedirecting || !s.isAuthenticated;
5217
5225
  }
5218
5226
  __name(resolveGuardIsLoading, "resolveGuardIsLoading");