You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Demonstrating Proof-of-Possession** –or just **DPoP**– is an OAuth 2.0 extension defined in [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449).
346
+
**Demonstrating Proof-of-Possession** —or simply **DPoP**— is a recent OAuth 2.0 extension defined in [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449).
347
347
348
-
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.**
348
+
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.**
349
349
350
-
In order to support DPoP in `auth0-spa-js`, we require some APIs found in modern browsers:
350
+
To support DPoP in `auth0-react`, some APIs available in modern browsers are required:
351
351
352
-
- [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.
352
+
- [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.
353
353
354
-
- [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).
354
+
- [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).
355
355
356
-
The following OAuth 2.0 flows are currently supported by `auth0-spa-js`:
356
+
The following OAuth 2.0 flows are currently supported by `auth0-react`:
Currently, only the `ES256` algorithm is supported.
364
+
> [!IMPORTANT]
365
+
> Currently, only the `ES256` algorithm is supported.
365
366
366
367
### Enabling DPoP
367
368
368
-
Currently, DPoP is disabled by default. To enable it, set the `useDpop` option to `true` when invoking the provider. For example:
369
+
DPoP is disabled by default. To enable it, set the `useDpop` option to `true` when invoking the provider. For example:
369
370
370
371
```jsx
371
372
<Auth0Provider
@@ -376,127 +377,197 @@ Currently, DPoP is disabled by default. To enable it, set the `useDpop` option t
376
377
>
377
378
```
378
379
379
-
After enabling DPoP, supported OAuth 2.0 flows in Auth0 will start transparently issuing tokens that will be cryptographically bound to the current browser.
380
+
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**.
380
381
381
-
Note that a DPoP token will have to be sent to a resource server with an `Authorization: DPoP <token>` header instead of `Authorization: Bearer <token>` as usual.
382
-
383
-
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:
382
+
> [!IMPORTANT]
383
+
> 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.
384
+
>
385
+
> You decide how to handle this transition. For example, you might require users to log in again the next time they use your application.
> Using DPoP requires storing some temporary data in the user's browser. When you log the user out with `logout()`, this data is deleted.
390
389
391
-
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.
390
+
> [!IMPORTANT]
391
+
> 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.
392
392
393
-
### Clearing DPoP data
393
+
### Using DPoP in your own requests
394
394
395
-
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.
395
+
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 <token>` header instead of the usual `Authorization: Bearer <token>`.
396
396
397
-
### Using DPoP in your own requests
397
+
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`.
398
398
399
-
Enabling `useDpop` **protects every internal request that the SDK sends to Auth0** (i.e. the authorization server).
399
+
For internal requests sent by `auth0-react` to Auth0, simply enable the `useDpop` option and **every interaction with Auth0 will be protected**.
400
400
401
-
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:
401
+
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:
402
402
403
403
- `getDpopNonce()`
404
404
- `setDpopNonce()`
405
405
- `generateDpopProof()`
406
406
407
-
This example shows how these coould be used:
407
+
However, due to the nature of how DPoP works, **this is not a trivial task**:
408
408
409
-
```jsx
410
-
import { useEffect, useState } from'react';
411
-
import { useAuth0 } from'@auth0/auth0-react';
409
+
- When a nonce is missing or expired, the request may need to be retried.
410
+
- Received nonces must be stored and managed.
411
+
- DPoP headers must be generated and included in every request, and regenerated for retries.
412
412
413
-
constPosts= () => {
414
-
const {
415
-
getAccessTokenSilently,
416
-
getDpopNonce,
417
-
setDpopNonce,
418
-
generateDpopProof,
419
-
} =useAuth0();
413
+
Because of this, we recommend using the provided `fetchWithAuth()` method, which **handles all of this for you**.
420
414
421
-
const [posts, setPosts] =useState(null);
415
+
#### Simple usage
422
416
423
-
useEffect(() => {
424
-
(async () => {
425
-
// Define an identifier that the SDK will use to reference the nonces.
426
-
constnonceId='my_api_request';
427
-
428
-
// Get an access token as usual.
429
-
constaccessToken=awaitgetAccessTokenSilently();
430
-
431
-
// Get the current DPoP nonce (if any) and do the request with it.
thrownewError('DPoP nonce was rejected twice, giving up');
474
-
}
475
-
476
-
returnfetchWithDpop({
477
-
...params,
478
-
nonce: newNonce ?? nonce,
479
-
isDpopNonceRetry:true,
480
-
});
481
-
}
482
-
483
-
return response;
484
-
}
485
-
})();
486
-
}, []);
417
+
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.
When using `fetchWithAuth()`, the following will be handled for you automatically:
452
+
453
+
- Use `getAccessTokenSilently()` to get the access token to inject in the headers.
454
+
- Generate and inject DPoP headers when needed.
455
+
- Store and update any DPoP nonces.
456
+
- Handle retries caused by a rejected nonce.
457
+
458
+
> [!IMPORTANT]
459
+
> 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.
460
+
461
+
#### Advanced usage
462
+
463
+
If you need something more complex than the example above, you can provide a custom implementation in the `fetch` property.
464
+
465
+
However, since `auth0-react` needs to make decisions based on HTTP responses, your implementation **must return an object with _at least_ two properties**:
466
+
467
+
1. `status`: the response status code as a number.
468
+
2. `headers`: the response headers as a plain object or as a Fetch API’s Headers-like interface.
469
+
470
+
Whatever it returns, it will be passed as the output of the `fetchWithAuth()` method.
471
+
472
+
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.
473
+
474
+
##### Example with `axios`
475
+
476
+
```js
477
+
const { createFetcher } =useAuth0();
478
+
479
+
constfetcher=createFetcher({
480
+
dpopNonceId:'my_api_request',
481
+
fetch: (request) =>
482
+
// The `Request` object has everything you need to do a request in a
483
+
// different library. Make sure that your output meets the requirements
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:
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**.
522
+
523
+
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:
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.
557
+
558
+
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:
0 commit comments