@versini/auth0-react-thin 2.23.0 → 2.24.0

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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as Auth0Provider, Auth0ProviderOptions, Auth0ProviderWithConfigOptions, Auth0ProviderWithClientOptions, AppState, ConnectedAccount } from './auth0-provider';
2
2
  export { default as useAuth0 } from './use-auth0';
3
+ export { default as useAuth0Suspense, Auth0SuspenseContextInterface, } from './use-auth0-suspense';
3
4
  export { default as withAuth0, WithAuth0Props } from './with-auth0';
4
5
  export { default as withAuthenticationRequired, WithAuthenticationRequiredOptions, } from './with-authentication-required';
5
6
  export { default as Auth0Context, Auth0ContextInterface, initialContext, LogoutOptions, RedirectLoginOptions, } from './auth0-context';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,IAAI,aAAa,EACxB,oBAAoB,EACpB,8BAA8B,EAC9B,8BAA8B,EAC9B,QAAQ,EACR,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,OAAO,IAAI,0BAA0B,EACrC,iCAAiC,GAClC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,OAAO,EACP,IAAI,EACJ,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,iBAAiB,EACjB,6BAA6B,EAC7B,4BAA4B,EAC5B,YAAY,EACZ,YAAY,EACZ,0BAA0B,EAC1B,qBAAqB,EACrB,QAAQ,EACR,mBAAmB,EACnB,gBAAgB,EAEhB,QAAQ,EACR,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,yBAAyB,EAEzB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EAEpB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,yBAAyB,EACzB,aAAa,EACb,uBAAuB,EAEvB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAEhB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EAEnB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,MAAM,EACN,iCAAiC,EACjC,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,IAAI,aAAa,EACxB,oBAAoB,EACpB,8BAA8B,EAC9B,8BAA8B,EAC9B,QAAQ,EACR,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EACL,OAAO,IAAI,gBAAgB,EAC3B,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,OAAO,IAAI,0BAA0B,EACrC,iCAAiC,GAClC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,OAAO,EACP,IAAI,EACJ,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,iBAAiB,EACjB,6BAA6B,EAC7B,4BAA4B,EAC5B,YAAY,EACZ,YAAY,EACZ,0BAA0B,EAC1B,qBAAqB,EACrB,QAAQ,EACR,mBAAmB,EACnB,gBAAgB,EAEhB,QAAQ,EACR,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,yBAAyB,EAEzB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EAEpB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,yBAAyB,EACzB,aAAa,EACb,uBAAuB,EAEvB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAEhB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EAEnB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,MAAM,EACN,iCAAiC,EACjC,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,35 @@
1
+ import * as React from 'react';
2
+ import { User } from '@auth0/auth0-spa-js';
3
+ import { Auth0ContextInterface } from './auth0-context';
4
+ /**
5
+ * The value returned by `useAuth0Suspense`: the full `useAuth0` interface minus
6
+ * `isLoading` and the internal `_initPromise`. `error` is
7
+ * retained for post-init failures such as `loginWithPopup`.
8
+ */
9
+ export type Auth0SuspenseContextInterface<TUser extends User = User> = Omit<Auth0ContextInterface<TUser>, 'isLoading' | '_initPromise'>;
10
+ /**
11
+ * ```jsx
12
+ * <Suspense fallback={<Spinner />}>
13
+ * <Profile />
14
+ * </Suspense>
15
+ *
16
+ * function Profile() {
17
+ * const { user, isAuthenticated } = useAuth0Suspense();
18
+ * return isAuthenticated ? <p>Hello {user.name}</p> : <p>Please log in</p>;
19
+ * }
20
+ * ```
21
+ *
22
+ * Suspense-enabled variant of `useAuth0`. Suspends the component until Auth0
23
+ * initialization completes (letting the nearest `<Suspense>` fallback render),
24
+ * and throws initialization errors so the nearest Error Boundary can handle
25
+ * them. Requires React 19 or later.
26
+ *
27
+ * If initialization fails and the app later becomes authenticated by other
28
+ * means, the session is re-checked once; retrying the Error Boundary then
29
+ * renders if that check succeeded, or throws again if it did not.
30
+ *
31
+ * TUser is an optional type param to provide a type to the `user` field.
32
+ */
33
+ declare const useAuth0Suspense: <TUser extends User = User>(context?: React.Context<Auth0ContextInterface<User>>) => Auth0SuspenseContextInterface<TUser>;
34
+ export default useAuth0Suspense;
35
+ //# sourceMappingURL=use-auth0-suspense.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-auth0-suspense.d.ts","sourceRoot":"","sources":["../src/use-auth0-suspense.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,IAAI,IAAI,CACzE,qBAAqB,CAAC,KAAK,CAAC,EAC5B,WAAW,GAAG,cAAc,CAC7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,gBAAgB,GAAI,KAAK,SAAS,IAAI,GAAG,IAAI,EACjD,oDAAsB,KACrB,6BAA6B,CAAC,KAAK,CA0BrC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "author": "Auth0",
3
3
  "name": "@versini/auth0-react-thin",
4
- "version": "2.23.0",
5
- "description": "Unofficial slim fork of @auth0/auth0-react@2.23.0 — bundle-size build, not affiliated with Auth0/Okta. See @versini/auth0-thin.",
4
+ "version": "2.24.0",
5
+ "description": "Unofficial slim fork of @auth0/auth0-react@2.24.0 — bundle-size build, not affiliated with Auth0/Okta. See @versini/auth0-thin.",
6
6
  "keywords": [
7
7
  "auth0",
8
8
  "fork",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@auth0/auth0-spa-js": "^2.24.1"
36
36
  },
37
- "forkedFrom": "@auth0/auth0-react@2.23.0",
37
+ "forkedFrom": "@auth0/auth0-react@2.24.0",
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  }
@@ -474,6 +474,16 @@ export interface Auth0ContextInterface<TUser extends User = User>
474
474
  * ```
475
475
  */
476
476
  myAccount: MyAccountApiClient;
477
+
478
+ /**
479
+ * Internal. A promise that resolves when Auth0 initialization completes and
480
+ * rejects with the initialization error if it fails. Consumed by
481
+ * `useAuth0Suspense`, which also treats its absence as "no provider" — do not
482
+ * add a stub value for this field to `initialContext`. Not part of the
483
+ * supported public API.
484
+ * @ignore
485
+ */
486
+ _initPromise?: Promise<void>;
477
487
  }
478
488
 
479
489
  /**
@@ -157,6 +157,32 @@ const defaultOnRedirectCallback = (appState?: AppState): void => {
157
157
  );
158
158
  };
159
159
 
160
+ /**
161
+ * @ignore
162
+ */
163
+ interface InitDeferred {
164
+ promise: Promise<void>;
165
+ resolve: () => void;
166
+ reject: (error: Error) => void;
167
+ }
168
+
169
+ /**
170
+ * @ignore
171
+ */
172
+ const createInitDeferred = (): InitDeferred => {
173
+ let resolve!: () => void;
174
+ let reject!: (error: Error) => void;
175
+ const promise = new Promise<void>((res, rej) => {
176
+ resolve = res;
177
+ reject = rej;
178
+ });
179
+ // Avoid unhandled-rejection warnings when no one is consuming the promise
180
+ // (i.e. useAuth0Suspense is not used). useAuth0Suspense attaches its own
181
+ // handler via use().
182
+ promise.catch(() => undefined);
183
+ return { promise, resolve, reject };
184
+ };
185
+
160
186
  /**
161
187
  * ```jsx
162
188
  * <Auth0Provider
@@ -194,6 +220,17 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
194
220
  () => providedClient ?? new Auth0Client(toAuth0ClientOptions(clientOpts))
195
221
  );
196
222
  const [state, dispatch] = useReducer(reducer<TUser>, initialAuthState as AuthState<TUser>);
223
+ // In state so the init retry below can swap in a fresh, non-rejected promise.
224
+ // Without that swap the first rejection would re-throw on every later render,
225
+ // making Error Boundary retries useless.
226
+ const [initDeferred, setInitDeferred] = useState(createInitDeferred);
227
+ // Set only once initialization has genuinely rejected. State, not a ref, so
228
+ // the retry effect re-evaluates when init fails *after* the app has become
229
+ // authenticated by some other means.
230
+ const [initFailed, setInitFailed] = useState(false);
231
+ // Guards against retrying more than once: a retry that also fails would
232
+ // otherwise re-trigger the effect for as long as the user stays signed in.
233
+ const initRetried = useRef(false);
197
234
  const didInitialise = useRef(false);
198
235
 
199
236
  const handleError = useCallback((error: Error) => {
@@ -201,15 +238,15 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
201
238
  return error;
202
239
  }, []);
203
240
 
204
- useEffect(() => {
205
- if (didInitialise.current) {
206
- return;
207
- }
208
- didInitialise.current = true;
209
- (async (): Promise<void> => {
241
+ // Settles `deferred` according to what initialization actually did, so
242
+ // `_initPromise` never claims success that did not happen. `allowRedirect`
243
+ // is false on a retry: the redirect callback is single-use, so a retry can
244
+ // only re-verify the session.
245
+ const runInit = useCallback(
246
+ async (deferred: InitDeferred, allowRedirect: boolean): Promise<void> => {
210
247
  try {
211
248
  let user: TUser | undefined;
212
- if (hasAuthParams() && !skipRedirectCallback) {
249
+ if (allowRedirect && hasAuthParams() && !skipRedirectCallback) {
213
250
  const { appState = {}, response_type, ...result } = await client.handleRedirectCallback();
214
251
  user = await client.getUser();
215
252
  appState.response_type = response_type;
@@ -222,11 +259,44 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
222
259
  user = await client.getUser();
223
260
  }
224
261
  dispatch({ type: 'INITIALISED', user });
262
+ deferred.resolve();
225
263
  } catch (error) {
226
- handleError(loginError(error));
264
+ const err = loginError(error);
265
+ handleError(err);
266
+ setInitFailed(true);
267
+ deferred.reject(err);
227
268
  }
228
- })();
229
- }, [client, onRedirectCallback, skipRedirectCallback, handleError]);
269
+ },
270
+ [client, onRedirectCallback, skipRedirectCallback, handleError]
271
+ );
272
+
273
+ useEffect(() => {
274
+ if (didInitialise.current) {
275
+ return;
276
+ }
277
+ didInitialise.current = true;
278
+ // Always the first deferred: `didInitialise` means this only ever runs once,
279
+ // so a later swap by the retry effect re-runs this effect but returns above.
280
+ void runInit(initDeferred, true);
281
+ }, [runInit, initDeferred]);
282
+
283
+ // A rejected promise stays rejected forever, so React.use() would re-throw on
284
+ // every later render and an Error Boundary retry could never recover. Once
285
+ // init has failed but the app has since become authenticated by some other
286
+ // means (loginWithPopup, a silent token, a passkey login), re-run the session
287
+ // check against a fresh deferred. The promise then reflects a real
288
+ // checkSession() outcome rather than an assumption that things are fine --
289
+ // if the session is still bad it rejects again and the boundary keeps showing
290
+ // the error.
291
+ useEffect(() => {
292
+ if (!initFailed || initRetried.current || !state.isAuthenticated) {
293
+ return;
294
+ }
295
+ initRetried.current = true;
296
+ const retry = createInitDeferred();
297
+ setInitDeferred(retry);
298
+ void runInit(retry, false);
299
+ }, [initFailed, state.isAuthenticated, runInit]);
230
300
 
231
301
  const loginWithRedirect = useCallback(
232
302
  (opts?: RedirectLoginOptions): Promise<void> => {
@@ -485,6 +555,7 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
485
555
  mfa,
486
556
  passkey,
487
557
  myAccount,
558
+ _initPromise: initDeferred.promise,
488
559
  };
489
560
  }, [
490
561
  state,
@@ -508,6 +579,7 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
508
579
  mfa,
509
580
  passkey,
510
581
  myAccount,
582
+ initDeferred,
511
583
  ]);
512
584
 
513
585
  return <context.Provider value={contextValue}>{children}</context.Provider>;
package/src/index.tsx CHANGED
@@ -7,6 +7,10 @@ export {
7
7
  ConnectedAccount
8
8
  } from './auth0-provider';
9
9
  export { default as useAuth0 } from './use-auth0';
10
+ export {
11
+ default as useAuth0Suspense,
12
+ Auth0SuspenseContextInterface,
13
+ } from './use-auth0-suspense';
10
14
  export { default as withAuth0, WithAuth0Props } from './with-auth0';
11
15
  export {
12
16
  default as withAuthenticationRequired,
@@ -0,0 +1,71 @@
1
+ // Namespace import: `use` only exists as a named export from React 19, so
2
+ // `import { use }` fails at link time for React 16-18 consumers even if they
3
+ // never call this hook. Property access stays late-bound.
4
+ import * as React from 'react';
5
+ import { User } from '@auth0/auth0-spa-js';
6
+ import Auth0Context, { Auth0ContextInterface } from './auth0-context';
7
+
8
+ /**
9
+ * The value returned by `useAuth0Suspense`: the full `useAuth0` interface minus
10
+ * `isLoading` and the internal `_initPromise`. `error` is
11
+ * retained for post-init failures such as `loginWithPopup`.
12
+ */
13
+ export type Auth0SuspenseContextInterface<TUser extends User = User> = Omit<
14
+ Auth0ContextInterface<TUser>,
15
+ 'isLoading' | '_initPromise'
16
+ >;
17
+
18
+ /**
19
+ * ```jsx
20
+ * <Suspense fallback={<Spinner />}>
21
+ * <Profile />
22
+ * </Suspense>
23
+ *
24
+ * function Profile() {
25
+ * const { user, isAuthenticated } = useAuth0Suspense();
26
+ * return isAuthenticated ? <p>Hello {user.name}</p> : <p>Please log in</p>;
27
+ * }
28
+ * ```
29
+ *
30
+ * Suspense-enabled variant of `useAuth0`. Suspends the component until Auth0
31
+ * initialization completes (letting the nearest `<Suspense>` fallback render),
32
+ * and throws initialization errors so the nearest Error Boundary can handle
33
+ * them. Requires React 19 or later.
34
+ *
35
+ * If initialization fails and the app later becomes authenticated by other
36
+ * means, the session is re-checked once; retrying the Error Boundary then
37
+ * renders if that check succeeded, or throws again if it did not.
38
+ *
39
+ * TUser is an optional type param to provide a type to the `user` field.
40
+ */
41
+ const useAuth0Suspense = <TUser extends User = User>(
42
+ context = Auth0Context
43
+ ): Auth0SuspenseContextInterface<TUser> => {
44
+ if (typeof React.use !== 'function') {
45
+ throw new Error(
46
+ 'useAuth0Suspense requires React 19 or later (React.use is unavailable).'
47
+ );
48
+ }
49
+
50
+ const ctx = React.useContext(context) as Auth0ContextInterface<TUser>;
51
+
52
+ if (!ctx._initPromise) {
53
+ throw new Error(
54
+ 'useAuth0Suspense must be used within an <Auth0Provider>.'
55
+ );
56
+ }
57
+
58
+ // Suspends until the init promise resolves; re-throws if it rejected.
59
+ React.use(ctx._initPromise);
60
+
61
+ // Memoized so the returned object is referentially stable across renders,
62
+ // matching useAuth0, which hands back the provider's memoized context.
63
+ // Without this, `useEffect(..., [auth])` in a consumer re-runs every render.
64
+ return React.useMemo(() => {
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ const { isLoading, _initPromise, ...rest } = ctx;
67
+ return rest;
68
+ }, [ctx]);
69
+ };
70
+
71
+ export default useAuth0Suspense;