@versini/auth0-react-thin 2.22.1 → 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/README.md +1 -1
- package/dist/auth0-context.d.ts +9 -0
- package/dist/auth0-context.d.ts.map +1 -1
- package/dist/auth0-provider.d.ts +5 -0
- package/dist/auth0-provider.d.ts.map +1 -1
- package/dist/auth0-react.cjs.js +173 -42
- package/dist/auth0-react.cjs.js.map +1 -1
- package/dist/auth0-react.esm.js +160 -48
- package/dist/auth0-react.esm.js.map +1 -1
- package/dist/auth0-react.js +172 -42
- package/dist/auth0-react.js.map +1 -1
- package/dist/auth0-react.min.js +1 -1
- package/dist/auth0-react.min.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/use-auth0-suspense.d.ts +35 -0
- package/dist/use-auth0-suspense.d.ts.map +1 -0
- package/dist/with-authentication-required.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/auth0-context.tsx +10 -0
- package/src/auth0-provider.tsx +87 -10
- package/src/index.tsx +4 -0
- package/src/use-auth0-suspense.tsx +71 -0
- package/src/with-authentication-required.tsx +8 -2
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-authentication-required.d.ts","sourceRoot":"","sources":["../src/with-authentication-required.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAa,EAAE,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAqB,EACnB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"with-authentication-required.d.ts","sourceRoot":"","sources":["../src/with-authentication-required.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAa,EAAE,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAqB,EACnB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAwBzB;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACnC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;IACxC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,QAAA,MAAM,0BAA0B,GAAI,CAAC,SAAS,MAAM,EAClD,WAAW,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAS,iCAAsC,KAC9C,EAAE,CAAC,CAAC,CAuCN,CAAC;AAEF,eAAe,0BAA0B,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.
|
|
5
|
-
"description": "Unofficial slim fork of @auth0/auth0-react@2.
|
|
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.
|
|
37
|
+
"forkedFrom": "@auth0/auth0-react@2.24.0",
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
}
|
package/src/auth0-context.tsx
CHANGED
|
@@ -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
|
/**
|
package/src/auth0-provider.tsx
CHANGED
|
@@ -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
|
|
@@ -168,6 +194,11 @@ const defaultOnRedirectCallback = (appState?: AppState): void => {
|
|
|
168
194
|
* ```
|
|
169
195
|
*
|
|
170
196
|
* Provides the Auth0Context to its child components.
|
|
197
|
+
*
|
|
198
|
+
* The SDK is a client-only module (its published ESM and CommonJS bundles ship the
|
|
199
|
+
* `'use client'` directive), so in a Next.js App Router app you can import and render
|
|
200
|
+
* `Auth0Provider` directly from a Server Component such as `app/layout.tsx`.
|
|
201
|
+
* Components that call hooks like `useAuth0` must still be Client Components.
|
|
171
202
|
*/
|
|
172
203
|
const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUser>) => {
|
|
173
204
|
const {
|
|
@@ -189,6 +220,17 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
|
|
|
189
220
|
() => providedClient ?? new Auth0Client(toAuth0ClientOptions(clientOpts))
|
|
190
221
|
);
|
|
191
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);
|
|
192
234
|
const didInitialise = useRef(false);
|
|
193
235
|
|
|
194
236
|
const handleError = useCallback((error: Error) => {
|
|
@@ -196,15 +238,15 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
|
|
|
196
238
|
return error;
|
|
197
239
|
}, []);
|
|
198
240
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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> => {
|
|
205
247
|
try {
|
|
206
248
|
let user: TUser | undefined;
|
|
207
|
-
if (hasAuthParams() && !skipRedirectCallback) {
|
|
249
|
+
if (allowRedirect && hasAuthParams() && !skipRedirectCallback) {
|
|
208
250
|
const { appState = {}, response_type, ...result } = await client.handleRedirectCallback();
|
|
209
251
|
user = await client.getUser();
|
|
210
252
|
appState.response_type = response_type;
|
|
@@ -217,11 +259,44 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
|
|
|
217
259
|
user = await client.getUser();
|
|
218
260
|
}
|
|
219
261
|
dispatch({ type: 'INITIALISED', user });
|
|
262
|
+
deferred.resolve();
|
|
220
263
|
} catch (error) {
|
|
221
|
-
|
|
264
|
+
const err = loginError(error);
|
|
265
|
+
handleError(err);
|
|
266
|
+
setInitFailed(true);
|
|
267
|
+
deferred.reject(err);
|
|
222
268
|
}
|
|
223
|
-
}
|
|
224
|
-
|
|
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]);
|
|
225
300
|
|
|
226
301
|
const loginWithRedirect = useCallback(
|
|
227
302
|
(opts?: RedirectLoginOptions): Promise<void> => {
|
|
@@ -480,6 +555,7 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
|
|
|
480
555
|
mfa,
|
|
481
556
|
passkey,
|
|
482
557
|
myAccount,
|
|
558
|
+
_initPromise: initDeferred.promise,
|
|
483
559
|
};
|
|
484
560
|
}, [
|
|
485
561
|
state,
|
|
@@ -503,6 +579,7 @@ const Auth0Provider = <TUser extends User = User>(opts: Auth0ProviderOptions<TUs
|
|
|
503
579
|
mfa,
|
|
504
580
|
passkey,
|
|
505
581
|
myAccount,
|
|
582
|
+
initDeferred,
|
|
506
583
|
]);
|
|
507
584
|
|
|
508
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;
|
|
@@ -18,8 +18,14 @@ const defaultOnBeforeAuthentication = async (): Promise<void> => {/* noop */ };
|
|
|
18
18
|
/**
|
|
19
19
|
* @ignore
|
|
20
20
|
*/
|
|
21
|
-
const defaultReturnTo = (): string =>
|
|
22
|
-
|
|
21
|
+
const defaultReturnTo = (): string => {
|
|
22
|
+
// Normalize the pathname to prevent protocol-relative open redirects.
|
|
23
|
+
// A URL like https://app.example.com//evil.com produces a pathname of
|
|
24
|
+
// //evil.com, which routers (react-router, next.js, gatsby) interpret as a
|
|
25
|
+
// protocol-relative URL and redirect the user to an external host.
|
|
26
|
+
const pathname = window.location.pathname.replace(/^\/\/+/, '/');
|
|
27
|
+
return `${pathname}${window.location.search}`;
|
|
28
|
+
};
|
|
23
29
|
|
|
24
30
|
/**
|
|
25
31
|
* Options for the withAuthenticationRequired Higher Order Component
|