From 3fe465a19e0ec89116a17adfc95c53e2a30c103b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:46:11 +0200 Subject: [PATCH 1/9] Add support for DPoP --- EXAMPLES.md | 173 +++++++++++++++++++++++++++++- __mocks__/@auth0/auth0-spa-js.tsx | 6 ++ __tests__/auth-provider.test.tsx | 47 ++++++++ src/auth0-context.tsx | 43 ++++++++ src/auth0-provider.tsx | 23 ++++ 5 files changed, 288 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index cd29491f..98e16975 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -8,6 +8,7 @@ - [Protecting a route in a Next.js app (in SPA mode)](#protecting-a-route-in-a-nextjs-app-in-spa-mode) - [Use with Auth0 organizations](#use-with-auth0-organizations) - [Protecting a route with a claims check](#protecting-a-route-with-a-claims-check) +- [Device-bound tokens with DPoP](#device-bound-tokens-with-dpop) ## Use with a Class Component @@ -325,17 +326,181 @@ In order to protect a route with a claims check alongside an authentication requ ```jsx const withClaimCheck = (Component, myClaimCheckFunction, returnTo) => { - const { user } = useAuth0(); + const { user } = useAuth0(); if (myClaimCheckFunction(user)) { - return + return ; } Router.push(returnTo); -} +}; const checkClaims = (claim?: User) => claim?.['https://my.app.io/jwt/claims']?.ROLE?.includes('ADMIN'); // Usage const Page = withAuthenticationRequired( - withClaimCheck(Component, checkClaims, '/missing-roles' ) + withClaimCheck(Component, checkClaims, '/missing-roles') ); ``` + +## Device-bound tokens with DPoP + +**Demonstrating Proof-of-Possession** –or just **DPoP**– is an OAuth 2.0 extension defined in [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449). + +It defines a mechanism for securely binding tokens to a specific device by means of cryptographic signatures. Without it, **a token leak caused by XSS or other vulnerability could result in an attacker impersonating the real user.** + +In order to support DPoP in `auth0-spa-js`, we require some APIs found in modern browsers: + +- [Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Crypto): it allows to create and use cryptographic keys that will be used for creating the proofs (i.e. signatures) used in DPoP. + +- [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API): it allows to use cryptographic keys [without giving access to the private material](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto#storing_keys). + +The following OAuth 2.0 flows are currently supported by `auth0-spa-js`: + +- [Authorization Code Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow) (`authorization_code`). + +- [Refresh Token Flow](https://auth0.com/docs/secure/tokens/refresh-tokens) (`refresh_token`). + +- [Custom Token Exchange Flow](https://auth0.com/docs/authenticate/custom-token-exchange) (`urn:ietf:params:oauth:grant-type:token-exchange`). + +Currently, only the `ES256` algorithm is supported. + +### Enabling DPoP + +Currently, DPoP is disabled by default. To enable it, set the `useDpop` option to `true` when invoking the provider. For example: + +```jsx + +``` + +After enabling DPoP, supported OAuth 2.0 flows in Auth0 will start transparently issuing tokens that will be cryptographically bound to the current browser. + +Note that a DPoP token will have to be sent to a resource server with an `Authorization: DPoP ` header instead of `Authorization: Bearer ` as usual. + +If you're using both types at the same time, you can use the `detailedResponse` option in `getAccessTokenSilently()` to get access to the `token_type` property and know what kind of token you got: + +```js +const headers = { + Authorization: `${token.token_type} ${token.access_token}`, +}; +``` + +If all your clients are already using DPoP, you may want to increase security and make Auth0 reject non-DPoP interactions by enabling the "Require Token Sender-Constraining" option in your Auth0's application settings. Check [the docs](https://auth0.com/docs/get-started/applications/configure-sender-constraining) for details. + +### Clearing DPoP data + +When using DPoP some temporary data is stored in the user's browser. When you log the user out with `logout()`, it will be deleted. + +### Using DPoP in your own requests + +Enabling `useDpop` **protects every internal request that the SDK sends to Auth0** (i.e. the authorization server). + +However, if you want to use a DPoP access token to authenticate against a custom API (i.e. a resource server), some extra work is required. `Auth0Provider` has some methods that will provide the needed pieces: + +- `getDpopNonce()` +- `setDpopNonce()` +- `generateDpopProof()` + +This example shows how these coould be used: + +```jsx +import { useEffect, useState } from 'react'; +import { useAuth0 } from '@auth0/auth0-react'; + +const Posts = () => { + const { + getAccessTokenSilently, + getDpopNonce, + setDpopNonce, + generateDpopProof, + } = useAuth0(); + + const [posts, setPosts] = useState(null); + + useEffect(() => { + (async () => { + // Define an identifier that the SDK will use to reference the nonces. + const nonceId = 'my_api_request'; + + // Get an access token as usual. + const accessToken = await getAccessTokenSilently(); + + // Get the current DPoP nonce (if any) and do the request with it. + const nonce = await getDpopNonce(nonceId); + + const response = await fetchWithDpop({ + url: 'https://api.example.com/posts', + method: 'GET', + accessToken, + nonce, + }); + + setPosts(await response.json()); + + async function fetchWithDpop({ + url, + method, + body, + accessToken, + nonce, + isDpopNonceRetry, + }) { + const headers = { + // A DPoP access token has the type `DPoP` and not `Bearer`. + Authorization: `DPoP ${accessToken}`, + + // Include the DPoP proof, which is cryptographic evidence that we + // are in possession of the same key that was used to get the token. + DPoP: await generateDpopProof({ url, method, nonce, accessToken }), + }; + + // Make the request. + const response = await fetch(url, { method, headers, body }); + + // If there was a nonce in the response, save it. + const newNonce = response.headers.get('dpop-nonce'); + + if (newNonce) { + setDpopNonce(newNonce, nonceId); + } + + // If the server rejects the DPoP nonce but it provides a new one, try + // the request one last time with the correct nonce. + if ( + response.status === 401 && + response.headers.get('www-authenticate')?.includes('use_dpop_nonce') + ) { + if (isDpopNonceRetry) { + throw new Error('DPoP nonce was rejected twice, giving up'); + } + + return fetchWithDpop({ + ...params, + nonce: newNonce ?? nonce, + isDpopNonceRetry: true, + }); + } + + return response; + } + })(); + }, []); + + if (!posts) { + return
Loading...
; + } + + return ( +
    + {posts.map((post, index) => { + return
  • {post}
  • ; + })} +
+ ); +}; + +export default Posts; +``` diff --git a/__mocks__/@auth0/auth0-spa-js.tsx b/__mocks__/@auth0/auth0-spa-js.tsx index b19548ad..20bd0fd6 100644 --- a/__mocks__/@auth0/auth0-spa-js.tsx +++ b/__mocks__/@auth0/auth0-spa-js.tsx @@ -10,6 +10,9 @@ const isAuthenticated = jest.fn(() => false); const loginWithPopup = jest.fn(); const loginWithRedirect = jest.fn(); const logout = jest.fn(); +const getDpopNonce = jest.fn(); +const setDpopNonce = jest.fn(); +const generateDpopProof = jest.fn(); export const Auth0Client = jest.fn(() => { return { @@ -25,5 +28,8 @@ export const Auth0Client = jest.fn(() => { loginWithPopup, loginWithRedirect, logout, + getDpopNonce, + setDpopNonce, + generateDpopProof, }; }); diff --git a/__tests__/auth-provider.test.tsx b/__tests__/auth-provider.test.tsx index ee506f8b..b224c148 100644 --- a/__tests__/auth-provider.test.tsx +++ b/__tests__/auth-provider.test.tsx @@ -522,6 +522,7 @@ describe('Auth0Provider', () => { access_token: '123', id_token: '456', expires_in: 2, + token_type: 'Bearer', }; (clientMock.getTokenSilently as jest.Mock).mockResolvedValue(tokenResponse); const wrapper = createWrapper(); @@ -940,6 +941,52 @@ describe('Auth0Provider', () => { }); }); + it('should provide a getDpopNonce method', async () => { + const wrapper = createWrapper(); + const { result } = renderHook( + () => useContext(Auth0Context), + { wrapper } + ); + + expect(result.current.getDpopNonce).toBeInstanceOf(Function); + await act(() => result.current.getDpopNonce()) + expect(clientMock.getDpopNonce).toHaveBeenCalled(); + }); + + it('should provide a setDpopNonce method', async () => { + const wrapper = createWrapper(); + const { result } = renderHook( + () => useContext(Auth0Context), + { wrapper } + ); + + const nonce = 'n-123456'; + const id = 'my-nonce'; + + expect(result.current.setDpopNonce).toBeInstanceOf(Function); + await act(() => result.current.setDpopNonce(nonce, id)) + expect(clientMock.setDpopNonce).toHaveBeenCalledWith(nonce, id); + }); + + it('should provide a generateDpopProof method', async () => { + const wrapper = createWrapper(); + const { result } = renderHook( + () => useContext(Auth0Context), + { wrapper } + ); + + const params = { + url: 'https://api.example.com/foo', + method: 'GET', + nonce: 'n-123456', + accessToken: 'at-123456', + }; + + expect(result.current.generateDpopProof).toBeInstanceOf(Function); + await act(() => result.current.generateDpopProof(params)) + expect(clientMock.generateDpopProof).toHaveBeenCalledWith(params); + }); + it('should not update context value after rerender with no state change', async () => { clientMock.getTokenSilently.mockReturnThis(); clientMock.getUser.mockResolvedValue({ name: 'foo' }); diff --git a/src/auth0-context.tsx b/src/auth0-context.tsx index 62d74d1e..ae4e1408 100644 --- a/src/auth0-context.tsx +++ b/src/auth0-context.tsx @@ -140,6 +140,46 @@ export interface Auth0ContextInterface * @param url The URL to that should be used to retrieve the `state` and `code` values. Defaults to `window.location.href` if not given. */ handleRedirectCallback: (url?: string) => Promise; + + /** + * Returns the current DPoP nonce used for making requests to Auth0. + * + * It can return `undefined` because when starting fresh it will not + * be populated until after the first response from the server. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + * + * @param nonce The nonce value. + * @param id The identifier of a nonce: if absent, it will set the nonce + * used for requests to Auth0. Otherwise, it will be used to + * select a specific non-Auth0 nonce. + */ + getDpopNonce(id?: string): Promise; + + /** + * Gets the current DPoP nonce used for making requests to Auth0. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + * + * @param nonce The nonce value. + * @param id The identifier of a nonce: if absent, it will set the nonce + * used for requests to Auth0. Otherwise, it will be used to + * select a specific non-Auth0 nonce. + */ + setDpopNonce(nonce: string, id?: string): Promise; + + /** + * Returns a string to be used to demonstrate possession of the private + * key used to cryptographically bind access tokens with DPoP. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + */ + generateDpopProof(params: { + url: string; + method: string; + nonce?: string; + accessToken: string; + }): Promise; } /** @@ -163,6 +203,9 @@ export const initialContext = { loginWithPopup: stub, logout: stub, handleRedirectCallback: stub, + getDpopNonce: stub, + setDpopNonce: stub, + generateDpopProof: stub, }; /** diff --git a/src/auth0-provider.tsx b/src/auth0-provider.tsx index 47df9472..b7b26557 100644 --- a/src/auth0-provider.tsx +++ b/src/auth0-provider.tsx @@ -272,6 +272,23 @@ const Auth0Provider = (opts: Auth0ProviderOptions client.getDpopNonce(), [client]); + + const setDpopNonce = useCallback( + (nonce: string, nonceId?: string) => client.setDpopNonce(nonce, nonceId), + [client] + ); + + const generateDpopProof = useCallback( + (params: { + url: string; + method: string; + nonce?: string; + accessToken: string; + }) => client.generateDpopProof(params), + [client] + ); + const contextValue = useMemo>(() => { return { ...state, @@ -282,6 +299,9 @@ const Auth0Provider = (opts: Auth0ProviderOptions(opts: Auth0ProviderOptions{children}; From 1b4c2632cd9c3c9ddced7d0b36fe2ba8ee65d260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Wed, 30 Jul 2025 21:46:39 +0200 Subject: [PATCH 2/9] Revert accidental auto-formatting changes --- EXAMPLES.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 98e16975..35ec91c7 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -326,18 +326,18 @@ In order to protect a route with a claims check alongside an authentication requ ```jsx const withClaimCheck = (Component, myClaimCheckFunction, returnTo) => { - const { user } = useAuth0(); + const { user } = useAuth0(); if (myClaimCheckFunction(user)) { - return ; + return } Router.push(returnTo); -}; +} const checkClaims = (claim?: User) => claim?.['https://my.app.io/jwt/claims']?.ROLE?.includes('ADMIN'); // Usage const Page = withAuthenticationRequired( - withClaimCheck(Component, checkClaims, '/missing-roles') + withClaimCheck(Component, checkClaims, '/missing-roles' ) ); ``` From 1a6f0dbf088fcfba9679ce250c6e73040b6842b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Thu, 31 Jul 2025 13:00:08 +0200 Subject: [PATCH 3/9] Fix bad parameter syntax in example --- EXAMPLES.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 35ec91c7..6651abe4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -440,14 +440,10 @@ const Posts = () => { setPosts(await response.json()); - async function fetchWithDpop({ - url, - method, - body, - accessToken, - nonce, - isDpopNonceRetry, - }) { + async function fetchWithDpop(params) { + const { url, method, body, accessToken, nonce, isDpopNonceRetry } = + params; + const headers = { // A DPoP access token has the type `DPoP` and not `Bearer`. Authorization: `DPoP ${accessToken}`, From a97412acdd2cbeed9a7e6ad543e0e1a92520be46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Wed, 13 Aug 2025 19:22:08 +0200 Subject: [PATCH 4/9] Add fetcher support and docs and simplify types --- EXAMPLES.md | 293 +++++++++++++++++++----------- __mocks__/@auth0/auth0-spa-js.tsx | 2 + __tests__/auth-provider.test.tsx | 32 ++-- src/auth0-context.tsx | 17 +- src/auth0-provider.tsx | 25 ++- src/index.tsx | 4 +- 6 files changed, 227 insertions(+), 146 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 6651abe4..85b39320 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -343,17 +343,17 @@ const Page = withAuthenticationRequired( ## Device-bound tokens with DPoP -**Demonstrating Proof-of-Possession** –or just **DPoP**– is an OAuth 2.0 extension defined in [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449). +**Demonstrating Proof-of-Possession** —or simply **DPoP**— is a recent OAuth 2.0 extension defined in [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449). -It defines a mechanism for securely binding tokens to a specific device by means of cryptographic signatures. Without it, **a token leak caused by XSS or other vulnerability could result in an attacker impersonating the real user.** +It defines a mechanism for securely binding tokens to a specific device using cryptographic signatures. Without it, **a token leak caused by XSS or other vulnerabilities could allow an attacker to impersonate the real user.** -In order to support DPoP in `auth0-spa-js`, we require some APIs found in modern browsers: +To support DPoP in `auth0-react`, some APIs available in modern browsers are required: -- [Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Crypto): it allows to create and use cryptographic keys that will be used for creating the proofs (i.e. signatures) used in DPoP. +- [Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Crypto): allows to create and use cryptographic keys, which are used to generate the proofs (i.e. signatures) required for DPoP. -- [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API): it allows to use cryptographic keys [without giving access to the private material](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto#storing_keys). +- [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API): enables the use of cryptographic keys [without exposing the private material](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto#storing_keys). -The following OAuth 2.0 flows are currently supported by `auth0-spa-js`: +The following OAuth 2.0 flows are currently supported by `auth0-react`: - [Authorization Code Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow) (`authorization_code`). @@ -361,11 +361,12 @@ The following OAuth 2.0 flows are currently supported by `auth0-spa-js`: - [Custom Token Exchange Flow](https://auth0.com/docs/authenticate/custom-token-exchange) (`urn:ietf:params:oauth:grant-type:token-exchange`). -Currently, only the `ES256` algorithm is supported. +> [!IMPORTANT] +> Currently, only the `ES256` algorithm is supported. ### Enabling DPoP -Currently, DPoP is disabled by default. To enable it, set the `useDpop` option to `true` when invoking the provider. For example: +DPoP is disabled by default. To enable it, set the `useDpop` option to `true` when invoking the provider. For example: ```jsx ``` -After enabling DPoP, supported OAuth 2.0 flows in Auth0 will start transparently issuing tokens that will be cryptographically bound to the current browser. +After enabling DPoP, **every new session using a supported OAuth 2.0 flow in Auth0 will begin transparently to use tokens that are cryptographically bound to the current browser**. -Note that a DPoP token will have to be sent to a resource server with an `Authorization: DPoP ` header instead of `Authorization: Bearer ` as usual. - -If you're using both types at the same time, you can use the `detailedResponse` option in `getAccessTokenSilently()` to get access to the `token_type` property and know what kind of token you got: +> [!IMPORTANT] +> DPoP will only be used for new user sessions created after enabling it. Any previously existing sessions will continue using non-DPoP tokens until the user logs in again. +> +> You decide how to handle this transition. For example, you might require users to log in again the next time they use your application. -```js -const headers = { - Authorization: `${token.token_type} ${token.access_token}`, -}; -``` +> [!IMPORTANT] +> Using DPoP requires storing some temporary data in the user's browser. When you log the user out with `logout()`, this data is deleted. -If all your clients are already using DPoP, you may want to increase security and make Auth0 reject non-DPoP interactions by enabling the "Require Token Sender-Constraining" option in your Auth0's application settings. Check [the docs](https://auth0.com/docs/get-started/applications/configure-sender-constraining) for details. +> [!IMPORTANT] +> If all your clients are already using DPoP, you may want to increase security by making Auth0 reject any non-DPoP interactions. See [the docs on Sender Constraining](https://auth0.com/docs/secure/sender-constraining/configure-sender-constraining) for details. -### Clearing DPoP data +### Using DPoP in your own requests -When using DPoP some temporary data is stored in the user's browser. When you log the user out with `logout()`, it will be deleted. +You use a DPoP token the same way as a "traditional" access token, except it must be sent to the server with an `Authorization: DPoP ` header instead of the usual `Authorization: Bearer `. -### Using DPoP in your own requests +To determine the type of a token, use the `detailedResponse` option in `getAccessTokenSilently()` to access the `token_type` property, which will be either `DPoP` or `Bearer`. -Enabling `useDpop` **protects every internal request that the SDK sends to Auth0** (i.e. the authorization server). +For internal requests sent by `auth0-react` to Auth0, simply enable the `useDpop` option and **every interaction with Auth0 will be protected**. -However, if you want to use a DPoP access token to authenticate against a custom API (i.e. a resource server), some extra work is required. `Auth0Provider` has some methods that will provide the needed pieces: +However, **to use DPoP with a custom, external API, some additional work is required**. The `useAuth()` hook provides some low-level methods to help with this: - `getDpopNonce()` - `setDpopNonce()` - `generateDpopProof()` -This example shows how these coould be used: +However, due to the nature of how DPoP works, **this is not a trivial task**: -```jsx -import { useEffect, useState } from 'react'; -import { useAuth0 } from '@auth0/auth0-react'; +- When a nonce is missing or expired, the request may need to be retried. +- Received nonces must be stored and managed. +- DPoP headers must be generated and included in every request, and regenerated for retries. -const Posts = () => { - const { - getAccessTokenSilently, - getDpopNonce, - setDpopNonce, - generateDpopProof, - } = useAuth0(); +Because of this, we recommend using the provided `fetchWithAuth()` method, which **handles all of this for you**. - const [posts, setPosts] = useState(null); +#### Simple usage - useEffect(() => { - (async () => { - // Define an identifier that the SDK will use to reference the nonces. - const nonceId = 'my_api_request'; - - // Get an access token as usual. - const accessToken = await getAccessTokenSilently(); - - // Get the current DPoP nonce (if any) and do the request with it. - const nonce = await getDpopNonce(nonceId); - - const response = await fetchWithDpop({ - url: 'https://api.example.com/posts', - method: 'GET', - accessToken, - nonce, - }); - - setPosts(await response.json()); - - async function fetchWithDpop(params) { - const { url, method, body, accessToken, nonce, isDpopNonceRetry } = - params; - - const headers = { - // A DPoP access token has the type `DPoP` and not `Bearer`. - Authorization: `DPoP ${accessToken}`, - - // Include the DPoP proof, which is cryptographic evidence that we - // are in possession of the same key that was used to get the token. - DPoP: await generateDpopProof({ url, method, nonce, accessToken }), - }; - - // Make the request. - const response = await fetch(url, { method, headers, body }); - - // If there was a nonce in the response, save it. - const newNonce = response.headers.get('dpop-nonce'); - - if (newNonce) { - setDpopNonce(newNonce, nonceId); - } - - // If the server rejects the DPoP nonce but it provides a new one, try - // the request one last time with the correct nonce. - if ( - response.status === 401 && - response.headers.get('www-authenticate')?.includes('use_dpop_nonce') - ) { - if (isDpopNonceRetry) { - throw new Error('DPoP nonce was rejected twice, giving up'); - } - - return fetchWithDpop({ - ...params, - nonce: newNonce ?? nonce, - isDpopNonceRetry: true, - }); - } - - return response; - } - })(); - }, []); +The `fetchWithAuth()` method is a drop-in replacement for the native `fetch()` function from the Fetch API, so if you're already using it, the change will be minimal. - if (!posts) { - return
Loading...
; - } +For example, if you had this code: - return ( -
    - {posts.map((post, index) => { - return
  • {post}
  • ; - })} -
- ); -}; +```js +await fetch('https://api.example.com/foo', { + method: 'GET', + headers: { 'user-agent': 'My Client 1.0' } +}); -export default Posts; +console.log(response.status); +console.log(response.headers); +console.log(await response.json()); +``` + +You would change it as follows: + +```js +const { createFetcher } = useAuth0(); + +const fetcher = createFetcher({ + dpopNonceId: 'my_api_request' +}); + +await fetcher.fetchWithAuth('https://api.example.com/foo', { + method: 'GET', + headers: { 'user-agent': 'My Client 1.0' } +}); + +console.log(response.status); +console.log(response.headers); +console.log(await response.json()); +``` + +When using `fetchWithAuth()`, the following will be handled for you automatically: + +- Use `getAccessTokenSilently()` to get the access token to inject in the headers. +- Generate and inject DPoP headers when needed. +- Store and update any DPoP nonces. +- Handle retries caused by a rejected nonce. + +> [!IMPORTANT] +> If DPoP is enabled in the provider, a `dpopNonceId` **must** be present in the `createFetcher()` parameters, since it’s used to keep track of the DPoP nonces for each request. + +#### Advanced usage + +If you need something more complex than the example above, you can provide a custom implementation in the `fetch` property. + +However, since `auth0-react` needs to make decisions based on HTTP responses, your implementation **must return an object with _at least_ two properties**: + +1. `status`: the response status code as a number. +2. `headers`: the response headers as a plain object or as a Fetch API’s Headers-like interface. + +Whatever it returns, it will be passed as the output of the `fetchWithAuth()` method. + +Your implementation will be called with a standard, ready-to-use [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object, which will contain any headers needed for authorization and DPoP usage (if enabled). Depending on your needs, you can use this object directly or treat it as a container with everything required to make the request your own way. + +##### Example with `axios` + +```js +const { createFetcher } = useAuth0(); + +const fetcher = createFetcher({ + dpopNonceId: 'my_api_request', + fetch: (request) => + // The `Request` object has everything you need to do a request in a + // different library. Make sure that your output meets the requirements + // about the `status` and `headers` properties. + axios.request({ + url: request.url, + method: request.method, + data: request.body, + headers: Object.fromEntries(request.headers), + timeout: 2000, + // etc. + }), + }, +}); + +const response = await fetcher.fetchWithAuth('https://api.example.com/foo', { + method: 'POST', + body: JSON.stringify({ name: 'John Doe' }), + headers: { 'user-agent': 'My Client 1.0' }, +}); + +console.log(response.status); +console.log(response.headers); +console.log(response.data); +``` + +##### Timeouts with native `fetch()` + +The Fetch API doesn’t support passing a timeout value directly; instead, you’re expected to use an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). For example: + +```js +const { createFetcher } = useAuth0(); + +const fetcher = createFetcher(); + +await fetcher.fetchWithAuth('https://api.example.com/foo', { + signal: AbortSignal.timeout(2000) +}); +``` + +This works, but if you define your request parameters statically when your app starts and then call `fetchWithAuth()` after an indeterminate amount of time, you'll find that **the request will timeout immediately**. This happens because the `AbortSignal` **starts counting time as soon as it is created**. + +To work around this, you can pass a thin wrapper over the native `fetch()` so that a new `AbortSignal` is created each time a request is made: + +```js +const { createFetcher } = useAuth0(); + +const fetcher = createFetcher({ + fetch: (request) => signal: AbortSignal.timeout(2000), +}); + +await fetcher.fetchWithAuth('https://api.example.com/foo'); +``` + +##### Having a base URL + +If you need to make requests to different endpoints of the same API, passing a `baseUrl` to `createFetcher()` can be useful: + +```js +const { createFetcher } = useAuth0(); + +const fetcher = createFetcher({ + baseUrl: 'https://api.example.com' +}); + +await fetcher.fetchWithAuth('/foo'); // => https://api.example.com/foo +await fetcher.fetchWithAuth('/bar'); // => https://api.example.com/bar +await fetcher.fetchWithAuth('/xyz'); // => https://api.example.com/xyz + +// If the passed URL is absolute, `baseUrl` will be ignored for convenience: +await fetcher.fetchWithAuth('https://other-api.example.com/foo'); +``` + +##### Passing an access token + +The `fetchWithAuth()` method assumes you’re using the SDK to get the access token for the request. This means that by default, it will always call `getAccessTokenSilently()` internally before making the request. + +However, if you already have an access token or need to pass specific parameters to `getAccessTokenSilently()`, you can override this behavior with a custom access token factory, like so: + +```js +const { createFetcher, getAccessTokenSilently } = useAuth0(); + +createFetcher({ + getAccessToken: () => + getAccessTokenSilently({ + authorizationParams: { + audience: '', + scope: '' + // etc. + } + }) +}); ``` diff --git a/__mocks__/@auth0/auth0-spa-js.tsx b/__mocks__/@auth0/auth0-spa-js.tsx index 20bd0fd6..713e5d6d 100644 --- a/__mocks__/@auth0/auth0-spa-js.tsx +++ b/__mocks__/@auth0/auth0-spa-js.tsx @@ -13,6 +13,7 @@ const logout = jest.fn(); const getDpopNonce = jest.fn(); const setDpopNonce = jest.fn(); const generateDpopProof = jest.fn(); +const createFetcher = jest.fn(); export const Auth0Client = jest.fn(() => { return { @@ -31,5 +32,6 @@ export const Auth0Client = jest.fn(() => { getDpopNonce, setDpopNonce, generateDpopProof, + createFetcher, }; }); diff --git a/__tests__/auth-provider.test.tsx b/__tests__/auth-provider.test.tsx index b224c148..143d6422 100644 --- a/__tests__/auth-provider.test.tsx +++ b/__tests__/auth-provider.test.tsx @@ -943,37 +943,28 @@ describe('Auth0Provider', () => { it('should provide a getDpopNonce method', async () => { const wrapper = createWrapper(); - const { result } = renderHook( - () => useContext(Auth0Context), - { wrapper } - ); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); expect(result.current.getDpopNonce).toBeInstanceOf(Function); - await act(() => result.current.getDpopNonce()) + await act(() => result.current.getDpopNonce()); expect(clientMock.getDpopNonce).toHaveBeenCalled(); }); it('should provide a setDpopNonce method', async () => { const wrapper = createWrapper(); - const { result } = renderHook( - () => useContext(Auth0Context), - { wrapper } - ); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); const nonce = 'n-123456'; const id = 'my-nonce'; expect(result.current.setDpopNonce).toBeInstanceOf(Function); - await act(() => result.current.setDpopNonce(nonce, id)) + await act(() => result.current.setDpopNonce(nonce, id)); expect(clientMock.setDpopNonce).toHaveBeenCalledWith(nonce, id); }); it('should provide a generateDpopProof method', async () => { const wrapper = createWrapper(); - const { result } = renderHook( - () => useContext(Auth0Context), - { wrapper } - ); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); const params = { url: 'https://api.example.com/foo', @@ -983,10 +974,21 @@ describe('Auth0Provider', () => { }; expect(result.current.generateDpopProof).toBeInstanceOf(Function); - await act(() => result.current.generateDpopProof(params)) + await act(() => result.current.generateDpopProof(params)); expect(clientMock.generateDpopProof).toHaveBeenCalledWith(params); }); + it('should provide a createFetcher method', async () => { + const wrapper = createWrapper(); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); + + const config = { dpopNonceId: 'my_dpop_nonce_test_id' }; + + expect(result.current.createFetcher).toBeInstanceOf(Function); + await act(() => result.current.createFetcher(config)); + expect(clientMock.createFetcher).toHaveBeenCalledWith(config); + }); + it('should not update context value after rerender with no state change', async () => { clientMock.getTokenSilently.mockReturnThis(); clientMock.getUser.mockResolvedValue({ name: 'foo' }); diff --git a/src/auth0-context.tsx b/src/auth0-context.tsx index ae4e1408..e9cab796 100644 --- a/src/auth0-context.tsx +++ b/src/auth0-context.tsx @@ -9,6 +9,7 @@ import { User, GetTokenSilentlyVerboseResponse, RedirectLoginOptions as SPARedirectLoginOptions, + type Auth0Client, } from '@auth0/auth0-spa-js'; import { createContext } from 'react'; import { AuthState, initialAuthState } from './auth-state'; @@ -150,11 +151,11 @@ export interface Auth0ContextInterface * It requires enabling the {@link Auth0ClientOptions.useDpop} option. * * @param nonce The nonce value. - * @param id The identifier of a nonce: if absent, it will set the nonce + * @param id The identifier of a nonce: if absent, it will get the nonce * used for requests to Auth0. Otherwise, it will be used to * select a specific non-Auth0 nonce. */ - getDpopNonce(id?: string): Promise; + getDpopNonce: Auth0Client['getDpopNonce']; /** * Gets the current DPoP nonce used for making requests to Auth0. @@ -166,7 +167,7 @@ export interface Auth0ContextInterface * used for requests to Auth0. Otherwise, it will be used to * select a specific non-Auth0 nonce. */ - setDpopNonce(nonce: string, id?: string): Promise; + setDpopNonce: Auth0Client['setDpopNonce']; /** * Returns a string to be used to demonstrate possession of the private @@ -174,12 +175,9 @@ export interface Auth0ContextInterface * * It requires enabling the {@link Auth0ClientOptions.useDpop} option. */ - generateDpopProof(params: { - url: string; - method: string; - nonce?: string; - accessToken: string; - }): Promise; + generateDpopProof: Auth0Client['generateDpopProof']; + + createFetcher: Auth0Client['createFetcher']; } /** @@ -206,6 +204,7 @@ export const initialContext = { getDpopNonce: stub, setDpopNonce: stub, generateDpopProof: stub, + createFetcher: stub, }; /** diff --git a/src/auth0-provider.tsx b/src/auth0-provider.tsx index b7b26557..c6bcd5aa 100644 --- a/src/auth0-provider.tsx +++ b/src/auth0-provider.tsx @@ -272,20 +272,23 @@ const Auth0Provider = (opts: Auth0ProviderOptions client.getDpopNonce(), [client]); + const getDpopNonce = useCallback( + (id) => client.getDpopNonce(id), + [client] + ); + + const setDpopNonce = useCallback( + (nonce, id) => client.setDpopNonce(nonce, id), + [client] + ); - const setDpopNonce = useCallback( - (nonce: string, nonceId?: string) => client.setDpopNonce(nonce, nonceId), + const generateDpopProof = useCallback( + (params) => client.generateDpopProof(params), [client] ); - const generateDpopProof = useCallback( - (params: { - url: string; - method: string; - nonce?: string; - accessToken: string; - }) => client.generateDpopProof(params), + const createFetcher = useCallback( + (config) => client.createFetcher(config), [client] ); @@ -302,6 +305,7 @@ const Auth0Provider = (opts: Auth0ProviderOptions(opts: Auth0ProviderOptions{children}; diff --git a/src/index.tsx b/src/index.tsx index 3b860ff1..94efde79 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -36,6 +36,8 @@ export { PopupTimeoutError, AuthenticationError, MissingRefreshTokenError, - GenericError + GenericError, + UseDpopNonceError, + type FetcherConfig } from '@auth0/auth0-spa-js'; export { OAuthError } from './errors'; From 9f672feececa3c776999e2ad37c415f9cf771933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:43:29 +0200 Subject: [PATCH 5/9] Improve docs --- EXAMPLES.md | 4 ++-- src/auth0-context.tsx | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 85b39320..2c568e6c 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -384,10 +384,10 @@ After enabling DPoP, **every new session using a supported OAuth 2.0 flow in Aut > > You decide how to handle this transition. For example, you might require users to log in again the next time they use your application. -> [!IMPORTANT] +> [!NOTE] > Using DPoP requires storing some temporary data in the user's browser. When you log the user out with `logout()`, this data is deleted. -> [!IMPORTANT] +> [!TIP] > If all your clients are already using DPoP, you may want to increase security by making Auth0 reject any non-DPoP interactions. See [the docs on Sender Constraining](https://auth0.com/docs/secure/sender-constraining/configure-sender-constraining) for details. ### Using DPoP in your own requests diff --git a/src/auth0-context.tsx b/src/auth0-context.tsx index e9cab796..8ddd046e 100644 --- a/src/auth0-context.tsx +++ b/src/auth0-context.tsx @@ -158,7 +158,7 @@ export interface Auth0ContextInterface getDpopNonce: Auth0Client['getDpopNonce']; /** - * Gets the current DPoP nonce used for making requests to Auth0. + * Sets the current DPoP nonce used for making requests to Auth0. * * It requires enabling the {@link Auth0ClientOptions.useDpop} option. * @@ -177,6 +177,14 @@ export interface Auth0ContextInterface */ generateDpopProof: Auth0Client['generateDpopProof']; + /** + * Returns a new `Fetcher` class that will contain a `fetchWithAuth()` method. + * This is a drop-in replacement for the Fetch API's `fetch()` method, but will + * handle certain authentication logic for you, like building the proper auth + * headers or managing DPoP nonces and retries automatically. + * + * Check the `EXAMPLES.md` file for a deeper look into this method. + */ createFetcher: Auth0Client['createFetcher']; } From c88e0dc3cca9ea4b45e3ba0c6952062bc5e78071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:04:15 +0200 Subject: [PATCH 6/9] Fix broken code in examples --- EXAMPLES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2c568e6c..0637ee52 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -526,7 +526,7 @@ To work around this, you can pass a thin wrapper over the native `fetch()` so th const { createFetcher } = useAuth0(); const fetcher = createFetcher({ - fetch: (request) => signal: AbortSignal.timeout(2000), + fetch: (request) => fetch(request, { signal: AbortSignal.timeout(2000) }) }); await fetcher.fetchWithAuth('https://api.example.com/foo'); From 78f4bc49d50fe8b9c2df57e2bc0351f78a1c112d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:04:38 +0200 Subject: [PATCH 7/9] Bring in `auth0-spa-js` with DPoP support (2.3.0 -> 2.4.0) --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52ad95af..42344612 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.4.0", "license": "MIT", "dependencies": { - "@auth0/auth0-spa-js": "^2.2.0" + "@auth0/auth0-spa-js": "^2.4.0" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", @@ -74,9 +74,9 @@ } }, "node_modules/@auth0/auth0-spa-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.3.0.tgz", - "integrity": "sha512-zAW6w79UO+G1+3AxboVQIUIZy05xluSOb1ymGg2dqG0pIi0JxEtZGec05BOf2LJ9SehzW4WeCYUQsYD9BjrVpQ==", + "version": "2.4.0", + "resolved": "https://a0us.jfrog.io/artifactory/api/npm/npm/@auth0/auth0-spa-js/-/auth0-spa-js-2.4.0.tgz", + "integrity": "sha512-Hq37s9r2FYPAcP/WsslcjAeZhPStnfV2gRmXhIqWTSG/acxFt7eS2+yU5VM3KPxdwtv9ZzlZgoMFIsggpRV0Dw==", "license": "MIT" }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 7d055e55..55628f9f 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,6 @@ "react-dom": "^16.11.0 || ^17 || ^18 || ^19" }, "dependencies": { - "@auth0/auth0-spa-js": "^2.2.0" + "@auth0/auth0-spa-js": "^2.4.0" } } From 001c30f6a3ac620eb98d86b80e189c085eecda6f Mon Sep 17 00:00:00 2001 From: Gyanesh Gouraw Date: Wed, 10 Sep 2025 20:05:48 +0530 Subject: [PATCH 8/9] fix: update package-lock.json to use correct npm registry --- package-lock.json | 394 +++++++++++----------------------------------- 1 file changed, 89 insertions(+), 305 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87466ac1..f3acbf50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,10 +74,14 @@ } }, "node_modules/@auth0/auth0-spa-js": { - "version": "2.4.0", - "resolved": "https://a0us.jfrog.io/artifactory/api/npm/npm/@auth0/auth0-spa-js/-/auth0-spa-js-2.4.0.tgz", - "integrity": "sha512-Hq37s9r2FYPAcP/WsslcjAeZhPStnfV2gRmXhIqWTSG/acxFt7eS2+yU5VM3KPxdwtv9ZzlZgoMFIsggpRV0Dw==", - "license": "MIT" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.4.1.tgz", + "integrity": "sha512-GE1XPLEgEUeqYBw5VcA52+3ubJDh4VKZFqdu0mr6h7anu+rFZb389cDbfpW145I23T1knZB/jvQtuRrkcLuT+Q==", + "dependencies": { + "browser-tabs-lock": "^1.2.15", + "dpop": "^2.1.1", + "es-cookie": "~1.3.2" + } }, "node_modules/@babel/code-frame": { "version": "7.26.2", @@ -588,8 +592,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -645,8 +647,6 @@ }, "node_modules/@gerrit0/mini-shiki": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.7.0.tgz", - "integrity": "sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg==", "dev": true, "license": "MIT", "dependencies": { @@ -1338,8 +1338,6 @@ }, "node_modules/@shikijs/engine-oniguruma": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.7.0.tgz", - "integrity": "sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==", "dev": true, "license": "MIT", "dependencies": { @@ -1349,8 +1347,6 @@ }, "node_modules/@shikijs/langs": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.7.0.tgz", - "integrity": "sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1359,8 +1355,6 @@ }, "node_modules/@shikijs/themes": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.7.0.tgz", - "integrity": "sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1369,8 +1363,6 @@ }, "node_modules/@shikijs/types": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.7.0.tgz", - "integrity": "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==", "dev": true, "license": "MIT", "dependencies": { @@ -1380,8 +1372,6 @@ }, "node_modules/@shikijs/vscode-textmate": { "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "dev": true, "license": "MIT" }, @@ -1505,7 +1495,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -1583,6 +1572,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/glob": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "dev": true, @@ -1593,8 +1591,6 @@ }, "node_modules/@types/hast": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1675,6 +1671,11 @@ "parse5": "^7.0.0" } }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.10.6", "dev": true, @@ -1685,8 +1686,6 @@ }, "node_modules/@types/react": { "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "dev": true, "license": "MIT", "dependencies": { @@ -1695,8 +1694,6 @@ }, "node_modules/@types/react-dom": { "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", - "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1730,8 +1727,6 @@ }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "dev": true, "license": "MIT" }, @@ -1759,8 +1754,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { @@ -1789,8 +1782,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -1798,16 +1789,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", - "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", + "version": "8.37.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -1819,170 +1808,11 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", - "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.42.0", - "@typescript-eslint/types": "^8.42.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", - "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", - "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", - "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", - "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.42.0", - "@typescript-eslint/tsconfig-utils": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", - "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.42.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/project-service": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { @@ -2003,8 +1833,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { @@ -2021,8 +1849,6 @@ }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -2038,8 +1864,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { @@ -2063,8 +1887,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -2077,8 +1899,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { @@ -2106,8 +1926,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2116,8 +1934,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -2132,8 +1948,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { @@ -2156,8 +1970,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { @@ -2174,8 +1986,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2512,8 +2322,6 @@ }, "node_modules/array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "license": "MIT", "engines": { @@ -3023,6 +2831,15 @@ "dev": true, "license": "ISC" }, + "node_modules/browser-tabs-lock": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz", + "integrity": "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==", + "hasInstallScript": true, + "dependencies": { + "lodash": ">=4.17.21" + } + }, "node_modules/browserslist": { "version": "4.24.4", "dev": true, @@ -4339,26 +4156,50 @@ } }, "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "version": "5.1.0", "dev": true, "license": "MIT", "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", "is-glob": "^4.0.1", "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", "slash": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/del/node_modules/globby": { + "version": "10.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/delayed-stream": { @@ -4425,8 +4266,6 @@ }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "license": "MIT", "dependencies": { @@ -4477,6 +4316,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dpop": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dpop/-/dpop-2.1.1.tgz", + "integrity": "sha512-J0Of2JTiM4h5si0tlbPQ/lkqfZ5wAEVkKYBhkwyyANnPJfWH4VsR5uIkZ+T+OSPIwDYUg1fbd5Mmodd25HjY1w==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "dev": true, @@ -4708,6 +4555,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-cookie": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", + "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==" + }, "node_modules/es-define-property": { "version": "1.0.1", "dev": true, @@ -5517,16 +5369,12 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.1", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5848,27 +5696,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.2.0", "dev": true, @@ -6596,8 +6423,6 @@ }, "node_modules/is-path-cwd": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true, "license": "MIT", "engines": { @@ -8123,8 +7948,6 @@ }, "node_modules/koa": { "version": "2.16.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.1.tgz", - "integrity": "sha512-umfX9d3iuSxTQP4pnzLOz0HKnPg0FaUUIKcye2lOiz3KPu1Y3M3xlz76dISdFPQs37P9eJz1wUpcTS6KDPn9fA==", "dev": true, "license": "MIT", "dependencies": { @@ -8432,8 +8255,6 @@ }, "node_modules/linkify-it": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8524,7 +8345,6 @@ }, "node_modules/lodash": { "version": "4.17.21", - "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { @@ -8717,8 +8537,6 @@ }, "node_modules/markdown-it": { "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "license": "MIT", "dependencies": { @@ -8743,8 +8561,6 @@ }, "node_modules/mdurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, "license": "MIT" }, @@ -9211,8 +9027,6 @@ }, "node_modules/oidc-provider": { "version": "8.8.1", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-8.8.1.tgz", - "integrity": "sha512-qVChpayTwojUREJxLkFofUSK8kiSRIdzPrVSsoGibqRHl/YO60ege94OZS8vh7zaK+zxcG/Gu8UMaYB5ulohCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9522,8 +9336,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", "engines": { @@ -9902,8 +9714,6 @@ }, "node_modules/punycode.js": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, "license": "MIT", "engines": { @@ -10034,8 +9844,6 @@ }, "node_modules/react": { "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "dev": true, "license": "MIT", "engines": { @@ -10044,8 +9852,6 @@ }, "node_modules/react-dom": { "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "dev": true, "license": "MIT", "dependencies": { @@ -10419,13 +10225,11 @@ } }, "node_modules/rollup-plugin-delete": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-delete/-/rollup-plugin-delete-2.2.0.tgz", - "integrity": "sha512-REKtDKWvjZlbrWpPvM9X/fadCs3E9I9ge27AK8G0e4bXwSLeABAAwtjiI1u3ihqZxk6mJeB2IVeSbH4DtOcw7A==", + "version": "2.1.0", "dev": true, "license": "MIT", "dependencies": { - "del": "^6.1.1" + "del": "^5.1.0" }, "engines": { "node": ">=10" @@ -10670,8 +10474,6 @@ }, "node_modules/scheduler": { "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "dev": true, "license": "MIT" }, @@ -10694,8 +10496,6 @@ }, "node_modules/semver": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -11511,9 +11311,7 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", - "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "version": "0.2.3", "dev": true, "license": "MIT", "engines": { @@ -11581,8 +11379,6 @@ }, "node_modules/ts-api-utils": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { @@ -11594,8 +11390,6 @@ }, "node_modules/ts-jest": { "version": "29.4.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11647,8 +11441,6 @@ }, "node_modules/ts-jest/node_modules/type-fest": { "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -11817,8 +11609,6 @@ }, "node_modules/typedoc": { "version": "0.28.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.7.tgz", - "integrity": "sha512-lpz0Oxl6aidFkmS90VQDQjk/Qf2iw0IUvFqirdONBdj7jPSN9mGXhy66BcGNDxx5ZMyKKiBVAREvPEzT6Uxipw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11863,8 +11653,6 @@ }, "node_modules/typescript": { "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11877,8 +11665,6 @@ }, "node_modules/uc.micro": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true, "license": "MIT" }, @@ -12561,8 +12347,6 @@ }, "node_modules/yaml": { "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, "license": "ISC", "bin": { From b612cb447d81b0b44efd23031b67e76fdfd26d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Melado?= <217925+martinml@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:51:26 +0200 Subject: [PATCH 9/9] Force 2.4.1 since 2.4.0 had wrong deps --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3acbf50..1ed60ce7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.4.0", "license": "MIT", "dependencies": { - "@auth0/auth0-spa-js": "^2.4.0" + "@auth0/auth0-spa-js": "^2.4.1" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", diff --git a/package.json b/package.json index e9e15e41..1f0b471a 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,6 @@ "react-dom": "^16.11.0 || ^17 || ^18 || ^19" }, "dependencies": { - "@auth0/auth0-spa-js": "^2.4.0" + "@auth0/auth0-spa-js": "^2.4.1" } }